diff --git a/bindings/make.jl b/bindings/make.jl index b16fa890..ad08542b 100644 --- a/bindings/make.jl +++ b/bindings/make.jl @@ -327,7 +327,91 @@ function mlir_dialects(version::VersionNumber) ("vector", "Vector.jl", ["Vector/IR/VectorOps.td"]), ("x86vector", "X86Vector.jl", ["X86Vector/X86Vector.td"]), ] - + elseif v"19" <= version < v"20" + [ + ("acc", "OpenACC.jl", ["OpenACC/OpenACCOps.td"]), + ("affine", "Affine.jl", ["Affine/IR/AffineOps.td"]), + ("amdgpu", "AMDGPU.jl", ["AMDGPU/IR/AMDGPU.td"]), + ("amx", "AMX.jl", ["AMX/AMX.td"]), + ("arith", "Arith.jl", ["Arith/IR/ArithOps.td"]), + ("arm_neon", "ArmNeon.jl", ["ArmNeon/ArmNeon.td"]), + ( + "arm_sme", + "ArmSME.jl", + ["ArmSME/IR/ArmSMEOps.td", "ArmSME/IR/ArmSMEIntrinsicOps.td"], + ), + ("arm_sve", "ArmSVE.jl", ["ArmSVE/IR/ArmSVE.td"]), + ("async", "Async.jl", ["Async/IR/AsyncOps.td"]), + ("bufferization", "Bufferization.jl", ["Bufferization/IR/BufferizationOps.td"]), + ("builtin", "Builtin.jl", ["../IR/BuiltinOps.td"]), + ("cf", "ControlFlow.jl", ["ControlFlow/IR/ControlFlowOps.td"]), + ("complex", "Complex.jl", ["Complex/IR/ComplexOps.td"]), + # ("dlti", "DLTI.jl", ["DLTI/DLTI.td"]), # TODO crashes + ("emitc", "EmitC.jl", ["EmitC/IR/EmitC.td"]), + ("func", "Func.jl", ["Func/IR/FuncOps.td"]), + ("gpu", "GPU.jl", ["GPU/IR/GPUOps.td"]), + ("index", "Index.jl", ["Index/IR/IndexOps.td"]), + ("irdl", "IRDL.jl", ["IRDL/IR/IRDLOps.td"]), + ( + "linalg", + "Linalg.jl", + ["Linalg/IR/LinalgOps.td", "Linalg/IR/LinalgStructuredOps.td"], + ), + ( + "llvm", + "LLVMIR.jl", + [ + "LLVMIR/LLVMOps.td", + "LLVMIR/LLVMIntrinsicOps.td", + "LLVMIR/NVVMOps.td", + "LLVMIR/ROCDLOps.td", + ], + ), + ("math", "Math.jl", ["Math/IR/MathOps.td"]), + ("memref", "MemRef.jl", ["MemRef/IR/MemRefOps.td"]), + ("mesh", "Mesh.jl", ["Mesh/IR/MeshOps.td"]), + ("ml_program", "MLProgram.jl", ["MLProgram/IR/MLProgramOps.td"]), + ("mpi", "MPI.jl", ["MPI/IR/MPIOps.td"]), + ("nvgpu", "NVGPU.jl", ["NVGPU/IR/NVGPU.td"]), + ("omp", "OpenMP.jl", ["OpenMP/OpenMPOps.td"]), + ("pdl_interp", "PDLInterp.jl", ["PDLInterp/IR/PDLInterpOps.td"]), + ("pdl", "PDL.jl", ["PDL/IR/PDLOps.td"]), + ("polynomial", "Polynomial.jl", ["Polynomial/IR/Polynomial.td"]), + # ("ptr", "Ptr.jl", ["Ptr/IR/PtrOps.td"]), + ("quant", "Quant.jl", ["Quant/QuantOps.td"]), + ("scf", "SCF.jl", ["SCF/IR/SCFOps.td"]), + ("shape", "Shape.jl", ["Shape/IR/ShapeOps.td"]), + ("sparse_tensor", "SparseTensor.jl", ["SparseTensor/IR/SparseTensorOps.td"]), + ("spirv", "SPIRV.jl", ["SPIRV/IR/SPIRVOps.td"]), + ("tensor", "Tensor.jl", ["Tensor/IR/TensorOps.td"]), + ("tosa", "Tosa.jl", ["Tosa/IR/TosaOps.td"]), + ( + "transform", + "Transform.jl", + [ + "Affine/TransformOps/AffineTransformOps.td", + "Bufferization/TransformOps/BufferizationTransformOps.td", + "Func/TransformOps/FuncTransformOps.td", + "GPU/TransformOps/GPUTransformOps.td", + "Linalg/TransformOps/LinalgMatchOps.td", + "Linalg/TransformOps/LinalgTransformOps.td", + "MemRef/TransformOps/MemRefTransformOps.td", + "NVGPU/TransformOps/NVGPUTransformOps.td", + "SCF/TransformOps/SCFTransformOps.td", + "SparseTensor/TransformOps/SparseTensorTransformOps.td", + "Tensor/TransformOps/TensorTransformOps.td", + "Transform/IR/TransformOps.td", + "Transform/DebugExtension/DebugExtensionOps.td", + "Transform/LoopExtension/LoopExtensionOps.td", + "Transform/PDLExtension/PDLExtensionOps.td", + "Vector/TransformOps/VectorTransformOps.td", + ], + ), + ("ub", "UB.jl", ["UB/IR/UBOps.td"]), + ("vector", "Vector.jl", ["Vector/IR/VectorOps.td"]), + ("x86vector", "X86Vector.jl", ["X86Vector/X86Vector.td"]), + ("xegpu", "XeGPU.jl", ["XeGPU/IR/XeGPUOps.td"]), + ] else error("Unsupported MLIR version: $version") end @@ -343,6 +427,7 @@ julia_llvm = [ (v"1.11", v"16.0.6+2"), (v"1.12", v"17.0.6+3"), (v"1.12", v"18.1.7+2"), + (v"1.12", v"19.1.1+0"), ] options = load_options(joinpath(@__DIR__, "wrap.toml")) diff --git a/src/API/19/libMLIR_h.jl b/src/API/19/libMLIR_h.jl new file mode 100644 index 00000000..f177a81c --- /dev/null +++ b/src/API/19/libMLIR_h.jl @@ -0,0 +1,9740 @@ +using CEnum + +const intptr_t = Clong + +""" + mlirStringRefCreate(str, length) + +Constructs a string reference from the pointer and length. The pointer need not reference to a null-terminated string. +""" +function mlirStringRefCreate(str, length) + @ccall (MLIR_C_PATH[]).mlirStringRefCreate(str::Cstring, length::Csize_t)::MlirStringRef +end + +""" + mlirStringRefCreateFromCString(str) + +Constructs a string reference from a null-terminated C string. Prefer [`mlirStringRefCreate`](@ref) if the length of the string is known. +""" +function mlirStringRefCreateFromCString(str) + @ccall (MLIR_C_PATH[]).mlirStringRefCreateFromCString(str::Cstring)::MlirStringRef +end + +""" + mlirStringRefEqual(string, other) + +Returns true if two string references are equal, false otherwise. +""" +function mlirStringRefEqual(string, other) + @ccall (MLIR_C_PATH[]).mlirStringRefEqual( + string::MlirStringRef, other::MlirStringRef + )::Bool +end + +# typedef void ( * MlirStringCallback ) ( MlirStringRef , void * ) +""" +A callback for returning string references. + +This function is called back by the functions that need to return a reference to the portion of the string with the following arguments: - an [`MlirStringRef`](@ref) representing the current portion of the string - a pointer to user data forwarded from the printing call. +""" +const MlirStringCallback = Ptr{Cvoid} + +""" + mlirLogicalResultIsSuccess(res) + +Checks if the given logical result represents a success. +""" +function mlirLogicalResultIsSuccess(res) + @ccall (MLIR_C_PATH[]).mlirLogicalResultIsSuccess(res::MlirLogicalResult)::Bool +end + +""" + mlirLogicalResultIsFailure(res) + +Checks if the given logical result represents a failure. +""" +function mlirLogicalResultIsFailure(res) + @ccall (MLIR_C_PATH[]).mlirLogicalResultIsFailure(res::MlirLogicalResult)::Bool +end + +""" + mlirLogicalResultSuccess() + +Creates a logical result representing a success. +""" +function mlirLogicalResultSuccess() + @ccall (MLIR_C_PATH[]).mlirLogicalResultSuccess()::MlirLogicalResult +end + +""" + mlirLogicalResultFailure() + +Creates a logical result representing a failure. +""" +function mlirLogicalResultFailure() + @ccall (MLIR_C_PATH[]).mlirLogicalResultFailure()::MlirLogicalResult +end + +""" + mlirLlvmThreadPoolCreate() + +Create an LLVM thread pool. This is reexported here to avoid directly pulling in the LLVM headers directly. +""" +function mlirLlvmThreadPoolCreate() + @ccall (MLIR_C_PATH[]).mlirLlvmThreadPoolCreate()::MlirLlvmThreadPool +end + +""" + mlirLlvmThreadPoolDestroy(pool) + +Destroy an LLVM thread pool. +""" +function mlirLlvmThreadPoolDestroy(pool) + @ccall (MLIR_C_PATH[]).mlirLlvmThreadPoolDestroy(pool::MlirLlvmThreadPool)::Cvoid +end + +""" + mlirTypeIDCreate(ptr) + +`ptr` must be 8 byte aligned and unique to a type valid for the duration of the returned type id's usage +""" +function mlirTypeIDCreate(ptr) + @ccall (MLIR_C_PATH[]).mlirTypeIDCreate(ptr::Ptr{Cvoid})::MlirTypeID +end + +""" + mlirTypeIDIsNull(typeID) + +Checks whether a type id is null. +""" +function mlirTypeIDIsNull(typeID) + @ccall (MLIR_C_PATH[]).mlirTypeIDIsNull(typeID::MlirTypeID)::Bool +end + +""" + mlirTypeIDEqual(typeID1, typeID2) + +Checks if two type ids are equal. +""" +function mlirTypeIDEqual(typeID1, typeID2) + @ccall (MLIR_C_PATH[]).mlirTypeIDEqual(typeID1::MlirTypeID, typeID2::MlirTypeID)::Bool +end + +""" + mlirTypeIDHashValue(typeID) + +Returns the hash value of the type id. +""" +function mlirTypeIDHashValue(typeID) + @ccall (MLIR_C_PATH[]).mlirTypeIDHashValue(typeID::MlirTypeID)::Csize_t +end + +""" + mlirTypeIDAllocatorCreate() + +Creates a type id allocator for dynamic type id creation +""" +function mlirTypeIDAllocatorCreate() + @ccall (MLIR_C_PATH[]).mlirTypeIDAllocatorCreate()::MlirTypeIDAllocator +end + +""" + mlirTypeIDAllocatorDestroy(allocator) + +Deallocates the allocator and all allocated type ids +""" +function mlirTypeIDAllocatorDestroy(allocator) + @ccall (MLIR_C_PATH[]).mlirTypeIDAllocatorDestroy(allocator::MlirTypeIDAllocator)::Cvoid +end + +""" + mlirTypeIDAllocatorAllocateTypeID(allocator) + +Allocates a type id that is valid for the lifetime of the allocator +""" +function mlirTypeIDAllocatorAllocateTypeID(allocator) + @ccall (MLIR_C_PATH[]).mlirTypeIDAllocatorAllocateTypeID( + allocator::MlirTypeIDAllocator + )::MlirTypeID +end + +""" + mlirContextCreate() + +Creates an MLIR context and transfers its ownership to the caller. This sets the default multithreading option (enabled). +""" +function mlirContextCreate() + @ccall (MLIR_C_PATH[]).mlirContextCreate()::MlirContext +end + +""" + mlirContextCreateWithThreading(threadingEnabled) + +Creates an MLIR context with an explicit setting of the multithreading setting and transfers its ownership to the caller. +""" +function mlirContextCreateWithThreading(threadingEnabled) + @ccall (MLIR_C_PATH[]).mlirContextCreateWithThreading( + threadingEnabled::Bool + )::MlirContext +end + +""" + mlirContextCreateWithRegistry(registry, threadingEnabled) + +Creates an MLIR context, setting the multithreading setting explicitly and pre-loading the dialects from the provided DialectRegistry. +""" +function mlirContextCreateWithRegistry(registry, threadingEnabled) + @ccall (MLIR_C_PATH[]).mlirContextCreateWithRegistry( + registry::MlirDialectRegistry, threadingEnabled::Bool + )::MlirContext +end + +""" + mlirContextEqual(ctx1, ctx2) + +Checks if two contexts are equal. +""" +function mlirContextEqual(ctx1, ctx2) + @ccall (MLIR_C_PATH[]).mlirContextEqual(ctx1::MlirContext, ctx2::MlirContext)::Bool +end + +""" + mlirContextIsNull(context) + +Checks whether a context is null. +""" +function mlirContextIsNull(context) + @ccall (MLIR_C_PATH[]).mlirContextIsNull(context::MlirContext)::Bool +end + +""" + mlirContextDestroy(context) + +Takes an MLIR context owned by the caller and destroys it. +""" +function mlirContextDestroy(context) + @ccall (MLIR_C_PATH[]).mlirContextDestroy(context::MlirContext)::Cvoid +end + +""" + mlirContextSetAllowUnregisteredDialects(context, allow) + +Sets whether unregistered dialects are allowed in this context. +""" +function mlirContextSetAllowUnregisteredDialects(context, allow) + @ccall (MLIR_C_PATH[]).mlirContextSetAllowUnregisteredDialects( + context::MlirContext, allow::Bool + )::Cvoid +end + +""" + mlirContextGetAllowUnregisteredDialects(context) + +Returns whether the context allows unregistered dialects. +""" +function mlirContextGetAllowUnregisteredDialects(context) + @ccall (MLIR_C_PATH[]).mlirContextGetAllowUnregisteredDialects( + context::MlirContext + )::Bool +end + +""" + mlirContextGetNumRegisteredDialects(context) + +Returns the number of dialects registered with the given context. A registered dialect will be loaded if needed by the parser. +""" +function mlirContextGetNumRegisteredDialects(context) + @ccall (MLIR_C_PATH[]).mlirContextGetNumRegisteredDialects( + context::MlirContext + )::intptr_t +end + +""" + mlirContextAppendDialectRegistry(ctx, registry) + +Append the contents of the given dialect registry to the registry associated with the context. +""" +function mlirContextAppendDialectRegistry(ctx, registry) + @ccall (MLIR_C_PATH[]).mlirContextAppendDialectRegistry( + ctx::MlirContext, registry::MlirDialectRegistry + )::Cvoid +end + +""" + mlirContextGetNumLoadedDialects(context) + +Returns the number of dialects loaded by the context. +""" +function mlirContextGetNumLoadedDialects(context) + @ccall (MLIR_C_PATH[]).mlirContextGetNumLoadedDialects(context::MlirContext)::intptr_t +end + +""" + mlirContextGetOrLoadDialect(context, name) + +Gets the dialect instance owned by the given context using the dialect namespace to identify it, loads (i.e., constructs the instance of) the dialect if necessary. If the dialect is not registered with the context, returns null. Use mlirContextLoadDialect to load an unregistered dialect. +""" +function mlirContextGetOrLoadDialect(context, name) + @ccall (MLIR_C_PATH[]).mlirContextGetOrLoadDialect( + context::MlirContext, name::MlirStringRef + )::MlirDialect +end + +""" + mlirContextEnableMultithreading(context, enable) + +Set threading mode (must be set to false to mlir-print-ir-after-all). +""" +function mlirContextEnableMultithreading(context, enable) + @ccall (MLIR_C_PATH[]).mlirContextEnableMultithreading( + context::MlirContext, enable::Bool + )::Cvoid +end + +""" + mlirContextLoadAllAvailableDialects(context) + +Eagerly loads all available dialects registered with a context, making them available for use for IR construction. +""" +function mlirContextLoadAllAvailableDialects(context) + @ccall (MLIR_C_PATH[]).mlirContextLoadAllAvailableDialects(context::MlirContext)::Cvoid +end + +""" + mlirContextIsRegisteredOperation(context, name) + +Returns whether the given fully-qualified operation (i.e. 'dialect.operation') is registered with the context. This will return true if the dialect is loaded and the operation is registered within the dialect. +""" +function mlirContextIsRegisteredOperation(context, name) + @ccall (MLIR_C_PATH[]).mlirContextIsRegisteredOperation( + context::MlirContext, name::MlirStringRef + )::Bool +end + +""" + mlirContextSetThreadPool(context, threadPool) + +Sets the thread pool of the context explicitly, enabling multithreading in the process. This API should be used to avoid re-creating thread pools in long-running applications that perform multiple compilations, see the C++ documentation for MLIRContext for details. +""" +function mlirContextSetThreadPool(context, threadPool) + @ccall (MLIR_C_PATH[]).mlirContextSetThreadPool( + context::MlirContext, threadPool::MlirLlvmThreadPool + )::Cvoid +end + +""" + mlirDialectGetContext(dialect) + +Returns the context that owns the dialect. +""" +function mlirDialectGetContext(dialect) + @ccall (MLIR_C_PATH[]).mlirDialectGetContext(dialect::MlirDialect)::MlirContext +end + +""" + mlirDialectIsNull(dialect) + +Checks if the dialect is null. +""" +function mlirDialectIsNull(dialect) + @ccall (MLIR_C_PATH[]).mlirDialectIsNull(dialect::MlirDialect)::Bool +end + +""" + mlirDialectEqual(dialect1, dialect2) + +Checks if two dialects that belong to the same context are equal. Dialects from different contexts will not compare equal. +""" +function mlirDialectEqual(dialect1, dialect2) + @ccall (MLIR_C_PATH[]).mlirDialectEqual( + dialect1::MlirDialect, dialect2::MlirDialect + )::Bool +end + +""" + mlirDialectGetNamespace(dialect) + +Returns the namespace of the given dialect. +""" +function mlirDialectGetNamespace(dialect) + @ccall (MLIR_C_PATH[]).mlirDialectGetNamespace(dialect::MlirDialect)::MlirStringRef +end + +""" + mlirDialectHandleGetNamespace(arg1) + +Returns the namespace associated with the provided dialect handle. +""" +function mlirDialectHandleGetNamespace(arg1) + @ccall (MLIR_C_PATH[]).mlirDialectHandleGetNamespace( + arg1::MlirDialectHandle + )::MlirStringRef +end + +""" + mlirDialectHandleInsertDialect(arg1, arg2) + +Inserts the dialect associated with the provided dialect handle into the provided dialect registry +""" +function mlirDialectHandleInsertDialect(arg1, arg2) + @ccall (MLIR_C_PATH[]).mlirDialectHandleInsertDialect( + arg1::MlirDialectHandle, arg2::MlirDialectRegistry + )::Cvoid +end + +""" + mlirDialectHandleRegisterDialect(arg1, arg2) + +Registers the dialect associated with the provided dialect handle. +""" +function mlirDialectHandleRegisterDialect(arg1, arg2) + @ccall (MLIR_C_PATH[]).mlirDialectHandleRegisterDialect( + arg1::MlirDialectHandle, arg2::MlirContext + )::Cvoid +end + +""" + mlirDialectHandleLoadDialect(arg1, arg2) + +Loads the dialect associated with the provided dialect handle. +""" +function mlirDialectHandleLoadDialect(arg1, arg2) + @ccall (MLIR_C_PATH[]).mlirDialectHandleLoadDialect( + arg1::MlirDialectHandle, arg2::MlirContext + )::MlirDialect +end + +""" + mlirDialectRegistryCreate() + +Creates a dialect registry and transfers its ownership to the caller. +""" +function mlirDialectRegistryCreate() + @ccall (MLIR_C_PATH[]).mlirDialectRegistryCreate()::MlirDialectRegistry +end + +""" + mlirDialectRegistryIsNull(registry) + +Checks if the dialect registry is null. +""" +function mlirDialectRegistryIsNull(registry) + @ccall (MLIR_C_PATH[]).mlirDialectRegistryIsNull(registry::MlirDialectRegistry)::Bool +end + +""" + mlirDialectRegistryDestroy(registry) + +Takes a dialect registry owned by the caller and destroys it. +""" +function mlirDialectRegistryDestroy(registry) + @ccall (MLIR_C_PATH[]).mlirDialectRegistryDestroy(registry::MlirDialectRegistry)::Cvoid +end + +""" + mlirLocationGetAttribute(location) + +Returns the underlying location attribute of this location. +""" +function mlirLocationGetAttribute(location) + @ccall (MLIR_C_PATH[]).mlirLocationGetAttribute(location::MlirLocation)::MlirAttribute +end + +""" + mlirLocationFromAttribute(attribute) + +Creates a location from a location attribute. +""" +function mlirLocationFromAttribute(attribute) + @ccall (MLIR_C_PATH[]).mlirLocationFromAttribute(attribute::MlirAttribute)::MlirLocation +end + +""" + mlirLocationFileLineColGet(context, filename, line, col) + +Creates an File/Line/Column location owned by the given context. +""" +function mlirLocationFileLineColGet(context, filename, line, col) + @ccall (MLIR_C_PATH[]).mlirLocationFileLineColGet( + context::MlirContext, filename::MlirStringRef, line::Cuint, col::Cuint + )::MlirLocation +end + +""" + mlirLocationCallSiteGet(callee, caller) + +Creates a call site location with a callee and a caller. +""" +function mlirLocationCallSiteGet(callee, caller) + @ccall (MLIR_C_PATH[]).mlirLocationCallSiteGet( + callee::MlirLocation, caller::MlirLocation + )::MlirLocation +end + +""" + mlirLocationFusedGet(ctx, nLocations, locations, metadata) + +Creates a fused location with an array of locations and metadata. +""" +function mlirLocationFusedGet(ctx, nLocations, locations, metadata) + @ccall (MLIR_C_PATH[]).mlirLocationFusedGet( + ctx::MlirContext, + nLocations::intptr_t, + locations::Ptr{MlirLocation}, + metadata::MlirAttribute, + )::MlirLocation +end + +""" + mlirLocationNameGet(context, name, childLoc) + +Creates a name location owned by the given context. Providing null location for childLoc is allowed and if childLoc is null location, then the behavior is the same as having unknown child location. +""" +function mlirLocationNameGet(context, name, childLoc) + @ccall (MLIR_C_PATH[]).mlirLocationNameGet( + context::MlirContext, name::MlirStringRef, childLoc::MlirLocation + )::MlirLocation +end + +""" + mlirLocationUnknownGet(context) + +Creates a location with unknown position owned by the given context. +""" +function mlirLocationUnknownGet(context) + @ccall (MLIR_C_PATH[]).mlirLocationUnknownGet(context::MlirContext)::MlirLocation +end + +""" + mlirLocationGetContext(location) + +Gets the context that a location was created with. +""" +function mlirLocationGetContext(location) + @ccall (MLIR_C_PATH[]).mlirLocationGetContext(location::MlirLocation)::MlirContext +end + +""" + mlirLocationIsNull(location) + +Checks if the location is null. +""" +function mlirLocationIsNull(location) + @ccall (MLIR_C_PATH[]).mlirLocationIsNull(location::MlirLocation)::Bool +end + +""" + mlirLocationEqual(l1, l2) + +Checks if two locations are equal. +""" +function mlirLocationEqual(l1, l2) + @ccall (MLIR_C_PATH[]).mlirLocationEqual(l1::MlirLocation, l2::MlirLocation)::Bool +end + +""" + mlirLocationPrint(location, callback, userData) + +Prints a location by sending chunks of the string representation and forwarding `userData to `callback`. Note that the callback may be called several times with consecutive chunks of the string. +""" +function mlirLocationPrint(location, callback, userData) + @ccall (MLIR_C_PATH[]).mlirLocationPrint( + location::MlirLocation, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirModuleCreateEmpty(location) + +Creates a new, empty module and transfers ownership to the caller. +""" +function mlirModuleCreateEmpty(location) + @ccall (MLIR_C_PATH[]).mlirModuleCreateEmpty(location::MlirLocation)::MlirModule +end + +""" + mlirModuleCreateParse(context, _module) + +Parses a module from the string and transfers ownership to the caller. +""" +function mlirModuleCreateParse(context, _module) + @ccall (MLIR_C_PATH[]).mlirModuleCreateParse( + context::MlirContext, _module::MlirStringRef + )::MlirModule +end + +""" + mlirModuleGetContext(_module) + +Gets the context that a module was created with. +""" +function mlirModuleGetContext(_module) + @ccall (MLIR_C_PATH[]).mlirModuleGetContext(_module::MlirModule)::MlirContext +end + +""" + mlirModuleGetBody(_module) + +Gets the body of the module, i.e. the only block it contains. +""" +function mlirModuleGetBody(_module) + @ccall (MLIR_C_PATH[]).mlirModuleGetBody(_module::MlirModule)::MlirBlock +end + +""" + mlirModuleIsNull(_module) + +Checks whether a module is null. +""" +function mlirModuleIsNull(_module) + @ccall (MLIR_C_PATH[]).mlirModuleIsNull(_module::MlirModule)::Bool +end + +""" + mlirModuleDestroy(_module) + +Takes a module owned by the caller and deletes it. +""" +function mlirModuleDestroy(_module) + @ccall (MLIR_C_PATH[]).mlirModuleDestroy(_module::MlirModule)::Cvoid +end + +""" + mlirModuleGetOperation(_module) + +Views the module as a generic operation. +""" +function mlirModuleGetOperation(_module) + @ccall (MLIR_C_PATH[]).mlirModuleGetOperation(_module::MlirModule)::MlirOperation +end + +""" + mlirModuleFromOperation(op) + +Views the generic operation as a module. The returned module is null when the input operation was not a ModuleOp. +""" +function mlirModuleFromOperation(op) + @ccall (MLIR_C_PATH[]).mlirModuleFromOperation(op::MlirOperation)::MlirModule +end + +""" + mlirOperationStateGet(name, loc) + +Constructs an operation state from a name and a location. +""" +function mlirOperationStateGet(name, loc) + @ccall (MLIR_C_PATH[]).mlirOperationStateGet( + name::MlirStringRef, loc::MlirLocation + )::MlirOperationState +end + +""" + mlirOperationStateAddResults(state, n, results) + +Adds a list of components to the operation state. +""" +function mlirOperationStateAddResults(state, n, results) + @ccall (MLIR_C_PATH[]).mlirOperationStateAddResults( + state::Ptr{MlirOperationState}, n::intptr_t, results::Ptr{MlirType} + )::Cvoid +end + +function mlirOperationStateAddOperands(state, n, operands) + @ccall (MLIR_C_PATH[]).mlirOperationStateAddOperands( + state::Ptr{MlirOperationState}, n::intptr_t, operands::Ptr{MlirValue} + )::Cvoid +end + +function mlirOperationStateAddOwnedRegions(state, n, regions) + @ccall (MLIR_C_PATH[]).mlirOperationStateAddOwnedRegions( + state::Ptr{MlirOperationState}, n::intptr_t, regions::Ptr{MlirRegion} + )::Cvoid +end + +function mlirOperationStateAddSuccessors(state, n, successors) + @ccall (MLIR_C_PATH[]).mlirOperationStateAddSuccessors( + state::Ptr{MlirOperationState}, n::intptr_t, successors::Ptr{MlirBlock} + )::Cvoid +end + +function mlirOperationStateAddAttributes(state, n, attributes) + @ccall (MLIR_C_PATH[]).mlirOperationStateAddAttributes( + state::Ptr{MlirOperationState}, n::intptr_t, attributes::Ptr{MlirNamedAttribute} + )::Cvoid +end + +""" + mlirOperationStateEnableResultTypeInference(state) + +Enables result type inference for the operation under construction. If enabled, then the caller must not have called [`mlirOperationStateAddResults`](@ref)(). Note that if enabled, the [`mlirOperationCreate`](@ref)() call is failable: it will return a null operation on inference failure and will emit diagnostics. +""" +function mlirOperationStateEnableResultTypeInference(state) + @ccall (MLIR_C_PATH[]).mlirOperationStateEnableResultTypeInference( + state::Ptr{MlirOperationState} + )::Cvoid +end + +""" + mlirAsmStateCreateForOperation(op, flags) + +Creates new AsmState, as with AsmState the IR should not be mutated in-between using this state. Must be freed with a call to [`mlirAsmStateDestroy`](@ref)(). +""" +function mlirAsmStateCreateForOperation(op, flags) + @ccall (MLIR_C_PATH[]).mlirAsmStateCreateForOperation( + op::MlirOperation, flags::MlirOpPrintingFlags + )::MlirAsmState +end + +""" + mlirAsmStateCreateForValue(value, flags) + +Creates new AsmState from value. Must be freed with a call to [`mlirAsmStateDestroy`](@ref)(). +""" +function mlirAsmStateCreateForValue(value, flags) + @ccall (MLIR_C_PATH[]).mlirAsmStateCreateForValue( + value::MlirValue, flags::MlirOpPrintingFlags + )::MlirAsmState +end + +""" + mlirAsmStateDestroy(state) + +Destroys printing flags created with mlirAsmStateCreate. +""" +function mlirAsmStateDestroy(state) + @ccall (MLIR_C_PATH[]).mlirAsmStateDestroy(state::MlirAsmState)::Cvoid +end + +""" + mlirOpPrintingFlagsCreate() + +Creates new printing flags with defaults, intended for customization. Must be freed with a call to [`mlirOpPrintingFlagsDestroy`](@ref)(). +""" +function mlirOpPrintingFlagsCreate() + @ccall (MLIR_C_PATH[]).mlirOpPrintingFlagsCreate()::MlirOpPrintingFlags +end + +""" + mlirOpPrintingFlagsDestroy(flags) + +Destroys printing flags created with [`mlirOpPrintingFlagsCreate`](@ref). +""" +function mlirOpPrintingFlagsDestroy(flags) + @ccall (MLIR_C_PATH[]).mlirOpPrintingFlagsDestroy(flags::MlirOpPrintingFlags)::Cvoid +end + +""" + mlirOpPrintingFlagsElideLargeElementsAttrs(flags, largeElementLimit) + +Enables the elision of large elements attributes by printing a lexically valid but otherwise meaningless form instead of the element data. The `largeElementLimit` is used to configure what is considered to be a "large" ElementsAttr by providing an upper limit to the number of elements. +""" +function mlirOpPrintingFlagsElideLargeElementsAttrs(flags, largeElementLimit) + @ccall (MLIR_C_PATH[]).mlirOpPrintingFlagsElideLargeElementsAttrs( + flags::MlirOpPrintingFlags, largeElementLimit::intptr_t + )::Cvoid +end + +""" + mlirOpPrintingFlagsElideLargeResourceString(flags, largeResourceLimit) + +Enables the elision of large resources strings by omitting them from the `dialect_resources` section. The `largeResourceLimit` is used to configure what is considered to be a "large" resource by providing an upper limit to the string size. +""" +function mlirOpPrintingFlagsElideLargeResourceString(flags, largeResourceLimit) + @ccall (MLIR_C_PATH[]).mlirOpPrintingFlagsElideLargeResourceString( + flags::MlirOpPrintingFlags, largeResourceLimit::intptr_t + )::Cvoid +end + +""" + mlirOpPrintingFlagsEnableDebugInfo(flags, enable, prettyForm) + +Enable or disable printing of debug information (based on `enable`). If 'prettyForm' is set to true, debug information is printed in a more readable 'pretty' form. Note: The IR generated with 'prettyForm' is not parsable. +""" +function mlirOpPrintingFlagsEnableDebugInfo(flags, enable, prettyForm) + @ccall (MLIR_C_PATH[]).mlirOpPrintingFlagsEnableDebugInfo( + flags::MlirOpPrintingFlags, enable::Bool, prettyForm::Bool + )::Cvoid +end + +""" + mlirOpPrintingFlagsPrintGenericOpForm(flags) + +Always print operations in the generic form. +""" +function mlirOpPrintingFlagsPrintGenericOpForm(flags) + @ccall (MLIR_C_PATH[]).mlirOpPrintingFlagsPrintGenericOpForm( + flags::MlirOpPrintingFlags + )::Cvoid +end + +""" + mlirOpPrintingFlagsUseLocalScope(flags) + +Use local scope when printing the operation. This allows for using the printer in a more localized and thread-safe setting, but may not necessarily be identical to what the IR will look like when dumping the full module. +""" +function mlirOpPrintingFlagsUseLocalScope(flags) + @ccall (MLIR_C_PATH[]).mlirOpPrintingFlagsUseLocalScope( + flags::MlirOpPrintingFlags + )::Cvoid +end + +""" + mlirOpPrintingFlagsAssumeVerified(flags) + +Do not verify the operation when using custom operation printers. +""" +function mlirOpPrintingFlagsAssumeVerified(flags) + @ccall (MLIR_C_PATH[]).mlirOpPrintingFlagsAssumeVerified( + flags::MlirOpPrintingFlags + )::Cvoid +end + +""" + mlirOpPrintingFlagsSkipRegions(flags) + +Skip printing regions. +""" +function mlirOpPrintingFlagsSkipRegions(flags) + @ccall (MLIR_C_PATH[]).mlirOpPrintingFlagsSkipRegions(flags::MlirOpPrintingFlags)::Cvoid +end + +""" + mlirBytecodeWriterConfigCreate() + +Creates new printing flags with defaults, intended for customization. Must be freed with a call to [`mlirBytecodeWriterConfigDestroy`](@ref)(). +""" +function mlirBytecodeWriterConfigCreate() + @ccall (MLIR_C_PATH[]).mlirBytecodeWriterConfigCreate()::MlirBytecodeWriterConfig +end + +""" + mlirBytecodeWriterConfigDestroy(config) + +Destroys printing flags created with [`mlirBytecodeWriterConfigCreate`](@ref). +""" +function mlirBytecodeWriterConfigDestroy(config) + @ccall (MLIR_C_PATH[]).mlirBytecodeWriterConfigDestroy( + config::MlirBytecodeWriterConfig + )::Cvoid +end + +""" + mlirBytecodeWriterConfigDesiredEmitVersion(flags, version) + +Sets the version to emit in the writer config. +""" +function mlirBytecodeWriterConfigDesiredEmitVersion(flags, version) + @ccall (MLIR_C_PATH[]).mlirBytecodeWriterConfigDesiredEmitVersion( + flags::MlirBytecodeWriterConfig, version::Int64 + )::Cvoid +end + +""" + mlirOperationCreate(state) + +Creates an operation and transfers ownership to the caller. Note that caller owned child objects are transferred in this call and must not be further used. Particularly, this applies to any regions added to the state (the implementation may invalidate any such pointers). + +This call can fail under the following conditions, in which case, it will return a null operation and emit diagnostics: - Result type inference is enabled and cannot be performed. +""" +function mlirOperationCreate(state) + @ccall (MLIR_C_PATH[]).mlirOperationCreate( + state::Ptr{MlirOperationState} + )::MlirOperation +end + +""" + mlirOperationCreateParse(context, sourceStr, sourceName) + +Parses an operation, giving ownership to the caller. If parsing fails a null operation will be returned, and an error diagnostic emitted. + +`sourceStr` may be either the text assembly format, or binary bytecode format. `sourceName` is used as the file name of the source; any IR without locations will get a `FileLineColLoc` location with `sourceName` as the file name. +""" +function mlirOperationCreateParse(context, sourceStr, sourceName) + @ccall (MLIR_C_PATH[]).mlirOperationCreateParse( + context::MlirContext, sourceStr::MlirStringRef, sourceName::MlirStringRef + )::MlirOperation +end + +""" + mlirOperationClone(op) + +Creates a deep copy of an operation. The operation is not inserted and ownership is transferred to the caller. +""" +function mlirOperationClone(op) + @ccall (MLIR_C_PATH[]).mlirOperationClone(op::MlirOperation)::MlirOperation +end + +""" + mlirOperationDestroy(op) + +Takes an operation owned by the caller and destroys it. +""" +function mlirOperationDestroy(op) + @ccall (MLIR_C_PATH[]).mlirOperationDestroy(op::MlirOperation)::Cvoid +end + +""" + mlirOperationRemoveFromParent(op) + +Removes the given operation from its parent block. The operation is not destroyed. The ownership of the operation is transferred to the caller. +""" +function mlirOperationRemoveFromParent(op) + @ccall (MLIR_C_PATH[]).mlirOperationRemoveFromParent(op::MlirOperation)::Cvoid +end + +""" + mlirOperationIsNull(op) + +Checks whether the underlying operation is null. +""" +function mlirOperationIsNull(op) + @ccall (MLIR_C_PATH[]).mlirOperationIsNull(op::MlirOperation)::Bool +end + +""" + mlirOperationEqual(op, other) + +Checks whether two operation handles point to the same operation. This does not perform deep comparison. +""" +function mlirOperationEqual(op, other) + @ccall (MLIR_C_PATH[]).mlirOperationEqual(op::MlirOperation, other::MlirOperation)::Bool +end + +""" + mlirOperationGetContext(op) + +Gets the context this operation is associated with +""" +function mlirOperationGetContext(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetContext(op::MlirOperation)::MlirContext +end + +""" + mlirOperationGetLocation(op) + +Gets the location of the operation. +""" +function mlirOperationGetLocation(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetLocation(op::MlirOperation)::MlirLocation +end + +""" + mlirOperationGetTypeID(op) + +Gets the type id of the operation. Returns null if the operation does not have a registered operation description. +""" +function mlirOperationGetTypeID(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetTypeID(op::MlirOperation)::MlirTypeID +end + +""" + mlirOperationGetName(op) + +Gets the name of the operation as an identifier. +""" +function mlirOperationGetName(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetName(op::MlirOperation)::MlirIdentifier +end + +""" + mlirOperationGetBlock(op) + +Gets the block that owns this operation, returning null if the operation is not owned. +""" +function mlirOperationGetBlock(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetBlock(op::MlirOperation)::MlirBlock +end + +""" + mlirOperationGetParentOperation(op) + +Gets the operation that owns this operation, returning null if the operation is not owned. +""" +function mlirOperationGetParentOperation(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetParentOperation(op::MlirOperation)::MlirOperation +end + +""" + mlirOperationGetNumRegions(op) + +Returns the number of regions attached to the given operation. +""" +function mlirOperationGetNumRegions(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetNumRegions(op::MlirOperation)::intptr_t +end + +""" + mlirOperationGetRegion(op, pos) + +Returns `pos`-th region attached to the operation. +""" +function mlirOperationGetRegion(op, pos) + @ccall (MLIR_C_PATH[]).mlirOperationGetRegion( + op::MlirOperation, pos::intptr_t + )::MlirRegion +end + +""" + mlirOperationGetNextInBlock(op) + +Returns an operation immediately following the given operation it its enclosing block. +""" +function mlirOperationGetNextInBlock(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetNextInBlock(op::MlirOperation)::MlirOperation +end + +""" + mlirOperationGetNumOperands(op) + +Returns the number of operands of the operation. +""" +function mlirOperationGetNumOperands(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetNumOperands(op::MlirOperation)::intptr_t +end + +""" + mlirOperationGetOperand(op, pos) + +Returns `pos`-th operand of the operation. +""" +function mlirOperationGetOperand(op, pos) + @ccall (MLIR_C_PATH[]).mlirOperationGetOperand( + op::MlirOperation, pos::intptr_t + )::MlirValue +end + +""" + mlirOperationSetOperand(op, pos, newValue) + +Sets the `pos`-th operand of the operation. +""" +function mlirOperationSetOperand(op, pos, newValue) + @ccall (MLIR_C_PATH[]).mlirOperationSetOperand( + op::MlirOperation, pos::intptr_t, newValue::MlirValue + )::Cvoid +end + +""" + mlirOperationSetOperands(op, nOperands, operands) + +Replaces the operands of the operation. +""" +function mlirOperationSetOperands(op, nOperands, operands) + @ccall (MLIR_C_PATH[]).mlirOperationSetOperands( + op::MlirOperation, nOperands::intptr_t, operands::Ptr{MlirValue} + )::Cvoid +end + +""" + mlirOperationGetNumResults(op) + +Returns the number of results of the operation. +""" +function mlirOperationGetNumResults(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetNumResults(op::MlirOperation)::intptr_t +end + +""" + mlirOperationGetResult(op, pos) + +Returns `pos`-th result of the operation. +""" +function mlirOperationGetResult(op, pos) + @ccall (MLIR_C_PATH[]).mlirOperationGetResult( + op::MlirOperation, pos::intptr_t + )::MlirValue +end + +""" + mlirOperationGetNumSuccessors(op) + +Returns the number of successor blocks of the operation. +""" +function mlirOperationGetNumSuccessors(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetNumSuccessors(op::MlirOperation)::intptr_t +end + +""" + mlirOperationGetSuccessor(op, pos) + +Returns `pos`-th successor of the operation. +""" +function mlirOperationGetSuccessor(op, pos) + @ccall (MLIR_C_PATH[]).mlirOperationGetSuccessor( + op::MlirOperation, pos::intptr_t + )::MlirBlock +end + +""" + mlirOperationSetSuccessor(op, pos, block) + +Set `pos`-th successor of the operation. +""" +function mlirOperationSetSuccessor(op, pos, block) + @ccall (MLIR_C_PATH[]).mlirOperationSetSuccessor( + op::MlirOperation, pos::intptr_t, block::MlirBlock + )::Cvoid +end + +""" + mlirOperationHasInherentAttributeByName(op, name) + +Returns true if this operation defines an inherent attribute with this name. Note: the attribute can be optional, so [`mlirOperationGetInherentAttributeByName`](@ref) can still return a null attribute. +""" +function mlirOperationHasInherentAttributeByName(op, name) + @ccall (MLIR_C_PATH[]).mlirOperationHasInherentAttributeByName( + op::MlirOperation, name::MlirStringRef + )::Bool +end + +""" + mlirOperationGetInherentAttributeByName(op, name) + +Returns an inherent attribute attached to the operation given its name. +""" +function mlirOperationGetInherentAttributeByName(op, name) + @ccall (MLIR_C_PATH[]).mlirOperationGetInherentAttributeByName( + op::MlirOperation, name::MlirStringRef + )::MlirAttribute +end + +""" + mlirOperationSetInherentAttributeByName(op, name, attr) + +Sets an inherent attribute by name, replacing the existing if it exists. This has no effect if "name" does not match an inherent attribute. +""" +function mlirOperationSetInherentAttributeByName(op, name, attr) + @ccall (MLIR_C_PATH[]).mlirOperationSetInherentAttributeByName( + op::MlirOperation, name::MlirStringRef, attr::MlirAttribute + )::Cvoid +end + +""" + mlirOperationGetNumDiscardableAttributes(op) + +Returns the number of discardable attributes attached to the operation. +""" +function mlirOperationGetNumDiscardableAttributes(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetNumDiscardableAttributes( + op::MlirOperation + )::intptr_t +end + +""" + mlirOperationGetDiscardableAttribute(op, pos) + +Return `pos`-th discardable attribute of the operation. +""" +function mlirOperationGetDiscardableAttribute(op, pos) + @ccall (MLIR_C_PATH[]).mlirOperationGetDiscardableAttribute( + op::MlirOperation, pos::intptr_t + )::MlirNamedAttribute +end + +""" + mlirOperationGetDiscardableAttributeByName(op, name) + +Returns a discardable attribute attached to the operation given its name. +""" +function mlirOperationGetDiscardableAttributeByName(op, name) + @ccall (MLIR_C_PATH[]).mlirOperationGetDiscardableAttributeByName( + op::MlirOperation, name::MlirStringRef + )::MlirAttribute +end + +""" + mlirOperationSetDiscardableAttributeByName(op, name, attr) + +Sets a discardable attribute by name, replacing the existing if it exists or adding a new one otherwise. The new `attr` Attribute is not allowed to be null, use [`mlirOperationRemoveDiscardableAttributeByName`](@ref) to remove an Attribute instead. +""" +function mlirOperationSetDiscardableAttributeByName(op, name, attr) + @ccall (MLIR_C_PATH[]).mlirOperationSetDiscardableAttributeByName( + op::MlirOperation, name::MlirStringRef, attr::MlirAttribute + )::Cvoid +end + +""" + mlirOperationRemoveDiscardableAttributeByName(op, name) + +Removes a discardable attribute by name. Returns false if the attribute was not found and true if removed. +""" +function mlirOperationRemoveDiscardableAttributeByName(op, name) + @ccall (MLIR_C_PATH[]).mlirOperationRemoveDiscardableAttributeByName( + op::MlirOperation, name::MlirStringRef + )::Bool +end + +""" + mlirOperationGetNumAttributes(op) + +Returns the number of attributes attached to the operation. Deprecated, please use `mlirOperationGetNumInherentAttributes` or [`mlirOperationGetNumDiscardableAttributes`](@ref). +""" +function mlirOperationGetNumAttributes(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetNumAttributes(op::MlirOperation)::intptr_t +end + +""" + mlirOperationGetAttribute(op, pos) + +Return `pos`-th attribute of the operation. Deprecated, please use `mlirOperationGetInherentAttribute` or [`mlirOperationGetDiscardableAttribute`](@ref). +""" +function mlirOperationGetAttribute(op, pos) + @ccall (MLIR_C_PATH[]).mlirOperationGetAttribute( + op::MlirOperation, pos::intptr_t + )::MlirNamedAttribute +end + +""" + mlirOperationGetAttributeByName(op, name) + +Returns an attribute attached to the operation given its name. Deprecated, please use [`mlirOperationGetInherentAttributeByName`](@ref) or [`mlirOperationGetDiscardableAttributeByName`](@ref). +""" +function mlirOperationGetAttributeByName(op, name) + @ccall (MLIR_C_PATH[]).mlirOperationGetAttributeByName( + op::MlirOperation, name::MlirStringRef + )::MlirAttribute +end + +""" + mlirOperationSetAttributeByName(op, name, attr) + +Sets an attribute by name, replacing the existing if it exists or adding a new one otherwise. Deprecated, please use [`mlirOperationSetInherentAttributeByName`](@ref) or [`mlirOperationSetDiscardableAttributeByName`](@ref). +""" +function mlirOperationSetAttributeByName(op, name, attr) + @ccall (MLIR_C_PATH[]).mlirOperationSetAttributeByName( + op::MlirOperation, name::MlirStringRef, attr::MlirAttribute + )::Cvoid +end + +""" + mlirOperationRemoveAttributeByName(op, name) + +Removes an attribute by name. Returns false if the attribute was not found and true if removed. Deprecated, please use `mlirOperationRemoveInherentAttributeByName` or [`mlirOperationRemoveDiscardableAttributeByName`](@ref). +""" +function mlirOperationRemoveAttributeByName(op, name) + @ccall (MLIR_C_PATH[]).mlirOperationRemoveAttributeByName( + op::MlirOperation, name::MlirStringRef + )::Bool +end + +""" + mlirOperationPrint(op, callback, userData) + +Prints an operation by sending chunks of the string representation and forwarding `userData to `callback`. Note that the callback may be called several times with consecutive chunks of the string. +""" +function mlirOperationPrint(op, callback, userData) + @ccall (MLIR_C_PATH[]).mlirOperationPrint( + op::MlirOperation, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirOperationPrintWithFlags(op, flags, callback, userData) + +Same as [`mlirOperationPrint`](@ref) but accepts flags controlling the printing behavior. +""" +function mlirOperationPrintWithFlags(op, flags, callback, userData) + @ccall (MLIR_C_PATH[]).mlirOperationPrintWithFlags( + op::MlirOperation, + flags::MlirOpPrintingFlags, + callback::MlirStringCallback, + userData::Ptr{Cvoid}, + )::Cvoid +end + +""" + mlirOperationPrintWithState(op, state, callback, userData) + +Same as [`mlirOperationPrint`](@ref) but accepts AsmState controlling the printing behavior as well as caching computed names. +""" +function mlirOperationPrintWithState(op, state, callback, userData) + @ccall (MLIR_C_PATH[]).mlirOperationPrintWithState( + op::MlirOperation, + state::MlirAsmState, + callback::MlirStringCallback, + userData::Ptr{Cvoid}, + )::Cvoid +end + +""" + mlirOperationWriteBytecode(op, callback, userData) + +Same as [`mlirOperationPrint`](@ref) but writing the bytecode format. +""" +function mlirOperationWriteBytecode(op, callback, userData) + @ccall (MLIR_C_PATH[]).mlirOperationWriteBytecode( + op::MlirOperation, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirOperationWriteBytecodeWithConfig(op, config, callback, userData) + +Same as [`mlirOperationWriteBytecode`](@ref) but with writer config and returns failure only if desired bytecode could not be honored. +""" +function mlirOperationWriteBytecodeWithConfig(op, config, callback, userData) + @ccall (MLIR_C_PATH[]).mlirOperationWriteBytecodeWithConfig( + op::MlirOperation, + config::MlirBytecodeWriterConfig, + callback::MlirStringCallback, + userData::Ptr{Cvoid}, + )::MlirLogicalResult +end + +""" + mlirOperationDump(op) + +Prints an operation to stderr. +""" +function mlirOperationDump(op) + @ccall (MLIR_C_PATH[]).mlirOperationDump(op::MlirOperation)::Cvoid +end + +""" + mlirOperationVerify(op) + +Verify the operation and return true if it passes, false if it fails. +""" +function mlirOperationVerify(op) + @ccall (MLIR_C_PATH[]).mlirOperationVerify(op::MlirOperation)::Bool +end + +""" + mlirOperationMoveAfter(op, other) + +Moves the given operation immediately after the other operation in its parent block. The given operation may be owned by the caller or by its current block. The other operation must belong to a block. In any case, the ownership is transferred to the block of the other operation. +""" +function mlirOperationMoveAfter(op, other) + @ccall (MLIR_C_PATH[]).mlirOperationMoveAfter( + op::MlirOperation, other::MlirOperation + )::Cvoid +end + +""" + mlirOperationMoveBefore(op, other) + +Moves the given operation immediately before the other operation in its parent block. The given operation may be owner by the caller or by its current block. The other operation must belong to a block. In any case, the ownership is transferred to the block of the other operation. +""" +function mlirOperationMoveBefore(op, other) + @ccall (MLIR_C_PATH[]).mlirOperationMoveBefore( + op::MlirOperation, other::MlirOperation + )::Cvoid +end + +""" + MlirWalkResult + +Operation walk result. +""" +@cenum MlirWalkResult::UInt32 begin + MlirWalkResultAdvance = 0x0000000000000000 + MlirWalkResultInterrupt = 0x0000000000000001 + MlirWalkResultSkip = 0x0000000000000002 +end + +""" + MlirWalkOrder + +Traversal order for operation walk. +""" +@cenum MlirWalkOrder::UInt32 begin + MlirWalkPreOrder = 0x0000000000000000 + MlirWalkPostOrder = 0x0000000000000001 +end + +# typedef MlirWalkResult ( * MlirOperationWalkCallback ) ( MlirOperation , void * userData ) +""" +Operation walker type. The handler is passed an (opaque) reference to an operation and a pointer to a `userData`. +""" +const MlirOperationWalkCallback = Ptr{Cvoid} + +""" + mlirOperationWalk(op, callback, userData, walkOrder) + +Walks operation `op` in `walkOrder` and calls `callback` on that operation. `*userData` is passed to the callback as well and can be used to tunnel some context or other data into the callback. +""" +function mlirOperationWalk(op, callback, userData, walkOrder) + @ccall (MLIR_C_PATH[]).mlirOperationWalk( + op::MlirOperation, + callback::MlirOperationWalkCallback, + userData::Ptr{Cvoid}, + walkOrder::MlirWalkOrder, + )::Cvoid +end + +""" + mlirRegionCreate() + +Creates a new empty region and transfers ownership to the caller. +""" +function mlirRegionCreate() + @ccall (MLIR_C_PATH[]).mlirRegionCreate()::MlirRegion +end + +""" + mlirRegionDestroy(region) + +Takes a region owned by the caller and destroys it. +""" +function mlirRegionDestroy(region) + @ccall (MLIR_C_PATH[]).mlirRegionDestroy(region::MlirRegion)::Cvoid +end + +""" + mlirRegionIsNull(region) + +Checks whether a region is null. +""" +function mlirRegionIsNull(region) + @ccall (MLIR_C_PATH[]).mlirRegionIsNull(region::MlirRegion)::Bool +end + +""" + mlirRegionEqual(region, other) + +Checks whether two region handles point to the same region. This does not perform deep comparison. +""" +function mlirRegionEqual(region, other) + @ccall (MLIR_C_PATH[]).mlirRegionEqual(region::MlirRegion, other::MlirRegion)::Bool +end + +""" + mlirRegionGetFirstBlock(region) + +Gets the first block in the region. +""" +function mlirRegionGetFirstBlock(region) + @ccall (MLIR_C_PATH[]).mlirRegionGetFirstBlock(region::MlirRegion)::MlirBlock +end + +""" + mlirRegionAppendOwnedBlock(region, block) + +Takes a block owned by the caller and appends it to the given region. +""" +function mlirRegionAppendOwnedBlock(region, block) + @ccall (MLIR_C_PATH[]).mlirRegionAppendOwnedBlock( + region::MlirRegion, block::MlirBlock + )::Cvoid +end + +""" + mlirRegionInsertOwnedBlock(region, pos, block) + +Takes a block owned by the caller and inserts it at `pos` to the given region. This is an expensive operation that linearly scans the region, prefer insertAfter/Before instead. +""" +function mlirRegionInsertOwnedBlock(region, pos, block) + @ccall (MLIR_C_PATH[]).mlirRegionInsertOwnedBlock( + region::MlirRegion, pos::intptr_t, block::MlirBlock + )::Cvoid +end + +""" + mlirRegionInsertOwnedBlockAfter(region, reference, block) + +Takes a block owned by the caller and inserts it after the (non-owned) reference block in the given region. The reference block must belong to the region. If the reference block is null, prepends the block to the region. +""" +function mlirRegionInsertOwnedBlockAfter(region, reference, block) + @ccall (MLIR_C_PATH[]).mlirRegionInsertOwnedBlockAfter( + region::MlirRegion, reference::MlirBlock, block::MlirBlock + )::Cvoid +end + +""" + mlirRegionInsertOwnedBlockBefore(region, reference, block) + +Takes a block owned by the caller and inserts it before the (non-owned) reference block in the given region. The reference block must belong to the region. If the reference block is null, appends the block to the region. +""" +function mlirRegionInsertOwnedBlockBefore(region, reference, block) + @ccall (MLIR_C_PATH[]).mlirRegionInsertOwnedBlockBefore( + region::MlirRegion, reference::MlirBlock, block::MlirBlock + )::Cvoid +end + +""" + mlirOperationGetFirstRegion(op) + +Returns first region attached to the operation. +""" +function mlirOperationGetFirstRegion(op) + @ccall (MLIR_C_PATH[]).mlirOperationGetFirstRegion(op::MlirOperation)::MlirRegion +end + +""" + mlirRegionGetNextInOperation(region) + +Returns the region immediately following the given region in its parent operation. +""" +function mlirRegionGetNextInOperation(region) + @ccall (MLIR_C_PATH[]).mlirRegionGetNextInOperation(region::MlirRegion)::MlirRegion +end + +""" + mlirRegionTakeBody(target, source) + +Moves the entire content of the source region to the target region. +""" +function mlirRegionTakeBody(target, source) + @ccall (MLIR_C_PATH[]).mlirRegionTakeBody(target::MlirRegion, source::MlirRegion)::Cvoid +end + +""" + mlirBlockCreate(nArgs, args, locs) + +Creates a new empty block with the given argument types and transfers ownership to the caller. +""" +function mlirBlockCreate(nArgs, args, locs) + @ccall (MLIR_C_PATH[]).mlirBlockCreate( + nArgs::intptr_t, args::Ptr{MlirType}, locs::Ptr{MlirLocation} + )::MlirBlock +end + +""" + mlirBlockDestroy(block) + +Takes a block owned by the caller and destroys it. +""" +function mlirBlockDestroy(block) + @ccall (MLIR_C_PATH[]).mlirBlockDestroy(block::MlirBlock)::Cvoid +end + +""" + mlirBlockDetach(block) + +Detach a block from the owning region and assume ownership. +""" +function mlirBlockDetach(block) + @ccall (MLIR_C_PATH[]).mlirBlockDetach(block::MlirBlock)::Cvoid +end + +""" + mlirBlockIsNull(block) + +Checks whether a block is null. +""" +function mlirBlockIsNull(block) + @ccall (MLIR_C_PATH[]).mlirBlockIsNull(block::MlirBlock)::Bool +end + +""" + mlirBlockEqual(block, other) + +Checks whether two blocks handles point to the same block. This does not perform deep comparison. +""" +function mlirBlockEqual(block, other) + @ccall (MLIR_C_PATH[]).mlirBlockEqual(block::MlirBlock, other::MlirBlock)::Bool +end + +""" + mlirBlockGetParentOperation(arg1) + +Returns the closest surrounding operation that contains this block. +""" +function mlirBlockGetParentOperation(arg1) + @ccall (MLIR_C_PATH[]).mlirBlockGetParentOperation(arg1::MlirBlock)::MlirOperation +end + +""" + mlirBlockGetParentRegion(block) + +Returns the region that contains this block. +""" +function mlirBlockGetParentRegion(block) + @ccall (MLIR_C_PATH[]).mlirBlockGetParentRegion(block::MlirBlock)::MlirRegion +end + +""" + mlirBlockGetNextInRegion(block) + +Returns the block immediately following the given block in its parent region. +""" +function mlirBlockGetNextInRegion(block) + @ccall (MLIR_C_PATH[]).mlirBlockGetNextInRegion(block::MlirBlock)::MlirBlock +end + +""" + mlirBlockGetFirstOperation(block) + +Returns the first operation in the block. +""" +function mlirBlockGetFirstOperation(block) + @ccall (MLIR_C_PATH[]).mlirBlockGetFirstOperation(block::MlirBlock)::MlirOperation +end + +""" + mlirBlockGetTerminator(block) + +Returns the terminator operation in the block or null if no terminator. +""" +function mlirBlockGetTerminator(block) + @ccall (MLIR_C_PATH[]).mlirBlockGetTerminator(block::MlirBlock)::MlirOperation +end + +""" + mlirBlockAppendOwnedOperation(block, operation) + +Takes an operation owned by the caller and appends it to the block. +""" +function mlirBlockAppendOwnedOperation(block, operation) + @ccall (MLIR_C_PATH[]).mlirBlockAppendOwnedOperation( + block::MlirBlock, operation::MlirOperation + )::Cvoid +end + +""" + mlirBlockInsertOwnedOperation(block, pos, operation) + +Takes an operation owned by the caller and inserts it as `pos` to the block. This is an expensive operation that scans the block linearly, prefer insertBefore/After instead. +""" +function mlirBlockInsertOwnedOperation(block, pos, operation) + @ccall (MLIR_C_PATH[]).mlirBlockInsertOwnedOperation( + block::MlirBlock, pos::intptr_t, operation::MlirOperation + )::Cvoid +end + +""" + mlirBlockInsertOwnedOperationAfter(block, reference, operation) + +Takes an operation owned by the caller and inserts it after the (non-owned) reference operation in the given block. If the reference is null, prepends the operation. Otherwise, the reference must belong to the block. +""" +function mlirBlockInsertOwnedOperationAfter(block, reference, operation) + @ccall (MLIR_C_PATH[]).mlirBlockInsertOwnedOperationAfter( + block::MlirBlock, reference::MlirOperation, operation::MlirOperation + )::Cvoid +end + +""" + mlirBlockInsertOwnedOperationBefore(block, reference, operation) + +Takes an operation owned by the caller and inserts it before the (non-owned) reference operation in the given block. If the reference is null, appends the operation. Otherwise, the reference must belong to the block. +""" +function mlirBlockInsertOwnedOperationBefore(block, reference, operation) + @ccall (MLIR_C_PATH[]).mlirBlockInsertOwnedOperationBefore( + block::MlirBlock, reference::MlirOperation, operation::MlirOperation + )::Cvoid +end + +""" + mlirBlockGetNumArguments(block) + +Returns the number of arguments of the block. +""" +function mlirBlockGetNumArguments(block) + @ccall (MLIR_C_PATH[]).mlirBlockGetNumArguments(block::MlirBlock)::intptr_t +end + +""" + mlirBlockAddArgument(block, type, loc) + +Appends an argument of the specified type to the block. Returns the newly added argument. +""" +function mlirBlockAddArgument(block, type, loc) + @ccall (MLIR_C_PATH[]).mlirBlockAddArgument( + block::MlirBlock, type::MlirType, loc::MlirLocation + )::MlirValue +end + +""" + mlirBlockEraseArgument(block, index) + +Erase the argument at 'index' and remove it from the argument list. +""" +function mlirBlockEraseArgument(block, index) + @ccall (MLIR_C_PATH[]).mlirBlockEraseArgument(block::MlirBlock, index::Cuint)::Cvoid +end + +""" + mlirBlockInsertArgument(block, pos, type, loc) + +Inserts an argument of the specified type at a specified index to the block. Returns the newly added argument. +""" +function mlirBlockInsertArgument(block, pos, type, loc) + @ccall (MLIR_C_PATH[]).mlirBlockInsertArgument( + block::MlirBlock, pos::intptr_t, type::MlirType, loc::MlirLocation + )::MlirValue +end + +""" + mlirBlockGetArgument(block, pos) + +Returns `pos`-th argument of the block. +""" +function mlirBlockGetArgument(block, pos) + @ccall (MLIR_C_PATH[]).mlirBlockGetArgument(block::MlirBlock, pos::intptr_t)::MlirValue +end + +""" + mlirBlockPrint(block, callback, userData) + +Prints a block by sending chunks of the string representation and forwarding `userData to `callback`. Note that the callback may be called several times with consecutive chunks of the string. +""" +function mlirBlockPrint(block, callback, userData) + @ccall (MLIR_C_PATH[]).mlirBlockPrint( + block::MlirBlock, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirValueIsNull(value) + +Returns whether the value is null. +""" +function mlirValueIsNull(value) + @ccall (MLIR_C_PATH[]).mlirValueIsNull(value::MlirValue)::Bool +end + +""" + mlirValueEqual(value1, value2) + +Returns 1 if two values are equal, 0 otherwise. +""" +function mlirValueEqual(value1, value2) + @ccall (MLIR_C_PATH[]).mlirValueEqual(value1::MlirValue, value2::MlirValue)::Bool +end + +""" + mlirValueIsABlockArgument(value) + +Returns 1 if the value is a block argument, 0 otherwise. +""" +function mlirValueIsABlockArgument(value) + @ccall (MLIR_C_PATH[]).mlirValueIsABlockArgument(value::MlirValue)::Bool +end + +""" + mlirValueIsAOpResult(value) + +Returns 1 if the value is an operation result, 0 otherwise. +""" +function mlirValueIsAOpResult(value) + @ccall (MLIR_C_PATH[]).mlirValueIsAOpResult(value::MlirValue)::Bool +end + +""" + mlirBlockArgumentGetOwner(value) + +Returns the block in which this value is defined as an argument. Asserts if the value is not a block argument. +""" +function mlirBlockArgumentGetOwner(value) + @ccall (MLIR_C_PATH[]).mlirBlockArgumentGetOwner(value::MlirValue)::MlirBlock +end + +""" + mlirBlockArgumentGetArgNumber(value) + +Returns the position of the value in the argument list of its block. +""" +function mlirBlockArgumentGetArgNumber(value) + @ccall (MLIR_C_PATH[]).mlirBlockArgumentGetArgNumber(value::MlirValue)::intptr_t +end + +""" + mlirBlockArgumentSetType(value, type) + +Sets the type of the block argument to the given type. +""" +function mlirBlockArgumentSetType(value, type) + @ccall (MLIR_C_PATH[]).mlirBlockArgumentSetType(value::MlirValue, type::MlirType)::Cvoid +end + +""" + mlirOpResultGetOwner(value) + +Returns an operation that produced this value as its result. Asserts if the value is not an op result. +""" +function mlirOpResultGetOwner(value) + @ccall (MLIR_C_PATH[]).mlirOpResultGetOwner(value::MlirValue)::MlirOperation +end + +""" + mlirOpResultGetResultNumber(value) + +Returns the position of the value in the list of results of the operation that produced it. +""" +function mlirOpResultGetResultNumber(value) + @ccall (MLIR_C_PATH[]).mlirOpResultGetResultNumber(value::MlirValue)::intptr_t +end + +""" + mlirValueGetType(value) + +Returns the type of the value. +""" +function mlirValueGetType(value) + @ccall (MLIR_C_PATH[]).mlirValueGetType(value::MlirValue)::MlirType +end + +""" + mlirValueSetType(value, type) + +Set the type of the value. +""" +function mlirValueSetType(value, type) + @ccall (MLIR_C_PATH[]).mlirValueSetType(value::MlirValue, type::MlirType)::Cvoid +end + +""" + mlirValueDump(value) + +Prints the value to the standard error stream. +""" +function mlirValueDump(value) + @ccall (MLIR_C_PATH[]).mlirValueDump(value::MlirValue)::Cvoid +end + +""" + mlirValuePrint(value, callback, userData) + +Prints a value by sending chunks of the string representation and forwarding `userData to `callback`. Note that the callback may be called several times with consecutive chunks of the string. +""" +function mlirValuePrint(value, callback, userData) + @ccall (MLIR_C_PATH[]).mlirValuePrint( + value::MlirValue, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirValuePrintAsOperand(value, state, callback, userData) + +Prints a value as an operand (i.e., the ValueID). +""" +function mlirValuePrintAsOperand(value, state, callback, userData) + @ccall (MLIR_C_PATH[]).mlirValuePrintAsOperand( + value::MlirValue, + state::MlirAsmState, + callback::MlirStringCallback, + userData::Ptr{Cvoid}, + )::Cvoid +end + +""" + mlirValueGetFirstUse(value) + +Returns an op operand representing the first use of the value, or a null op operand if there are no uses. +""" +function mlirValueGetFirstUse(value) + @ccall (MLIR_C_PATH[]).mlirValueGetFirstUse(value::MlirValue)::MlirOpOperand +end + +""" + mlirValueReplaceAllUsesOfWith(of, with) + +Replace all uses of 'of' value with the 'with' value, updating anything in the IR that uses 'of' to use the other value instead. When this returns there are zero uses of 'of'. +""" +function mlirValueReplaceAllUsesOfWith(of, with) + @ccall (MLIR_C_PATH[]).mlirValueReplaceAllUsesOfWith( + of::MlirValue, with::MlirValue + )::Cvoid +end + +""" + mlirOpOperandIsNull(opOperand) + +Returns whether the op operand is null. +""" +function mlirOpOperandIsNull(opOperand) + @ccall (MLIR_C_PATH[]).mlirOpOperandIsNull(opOperand::MlirOpOperand)::Bool +end + +""" + mlirOpOperandGetValue(opOperand) + +Returns the value of an op operand. +""" +function mlirOpOperandGetValue(opOperand) + @ccall (MLIR_C_PATH[]).mlirOpOperandGetValue(opOperand::MlirOpOperand)::MlirValue +end + +""" + mlirOpOperandGetOwner(opOperand) + +Returns the owner operation of an op operand. +""" +function mlirOpOperandGetOwner(opOperand) + @ccall (MLIR_C_PATH[]).mlirOpOperandGetOwner(opOperand::MlirOpOperand)::MlirOperation +end + +""" + mlirOpOperandGetOperandNumber(opOperand) + +Returns the operand number of an op operand. +""" +function mlirOpOperandGetOperandNumber(opOperand) + @ccall (MLIR_C_PATH[]).mlirOpOperandGetOperandNumber(opOperand::MlirOpOperand)::Cuint +end + +""" + mlirOpOperandGetNextUse(opOperand) + +Returns an op operand representing the next use of the value, or a null op operand if there is no next use. +""" +function mlirOpOperandGetNextUse(opOperand) + @ccall (MLIR_C_PATH[]).mlirOpOperandGetNextUse(opOperand::MlirOpOperand)::MlirOpOperand +end + +""" + mlirTypeParseGet(context, type) + +Parses a type. The type is owned by the context. +""" +function mlirTypeParseGet(context, type) + @ccall (MLIR_C_PATH[]).mlirTypeParseGet( + context::MlirContext, type::MlirStringRef + )::MlirType +end + +""" + mlirTypeGetContext(type) + +Gets the context that a type was created with. +""" +function mlirTypeGetContext(type) + @ccall (MLIR_C_PATH[]).mlirTypeGetContext(type::MlirType)::MlirContext +end + +""" + mlirTypeGetTypeID(type) + +Gets the type ID of the type. +""" +function mlirTypeGetTypeID(type) + @ccall (MLIR_C_PATH[]).mlirTypeGetTypeID(type::MlirType)::MlirTypeID +end + +""" + mlirTypeGetDialect(type) + +Gets the dialect a type belongs to. +""" +function mlirTypeGetDialect(type) + @ccall (MLIR_C_PATH[]).mlirTypeGetDialect(type::MlirType)::MlirDialect +end + +""" + mlirTypeIsNull(type) + +Checks whether a type is null. +""" +function mlirTypeIsNull(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsNull(type::MlirType)::Bool +end + +""" + mlirTypeEqual(t1, t2) + +Checks if two types are equal. +""" +function mlirTypeEqual(t1, t2) + @ccall (MLIR_C_PATH[]).mlirTypeEqual(t1::MlirType, t2::MlirType)::Bool +end + +""" + mlirTypePrint(type, callback, userData) + +Prints a location by sending chunks of the string representation and forwarding `userData to `callback`. Note that the callback may be called several times with consecutive chunks of the string. +""" +function mlirTypePrint(type, callback, userData) + @ccall (MLIR_C_PATH[]).mlirTypePrint( + type::MlirType, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirTypeDump(type) + +Prints the type to the standard error stream. +""" +function mlirTypeDump(type) + @ccall (MLIR_C_PATH[]).mlirTypeDump(type::MlirType)::Cvoid +end + +""" + mlirAttributeParseGet(context, attr) + +Parses an attribute. The attribute is owned by the context. +""" +function mlirAttributeParseGet(context, attr) + @ccall (MLIR_C_PATH[]).mlirAttributeParseGet( + context::MlirContext, attr::MlirStringRef + )::MlirAttribute +end + +""" + mlirAttributeGetContext(attribute) + +Gets the context that an attribute was created with. +""" +function mlirAttributeGetContext(attribute) + @ccall (MLIR_C_PATH[]).mlirAttributeGetContext(attribute::MlirAttribute)::MlirContext +end + +""" + mlirAttributeGetType(attribute) + +Gets the type of this attribute. +""" +function mlirAttributeGetType(attribute) + @ccall (MLIR_C_PATH[]).mlirAttributeGetType(attribute::MlirAttribute)::MlirType +end + +""" + mlirAttributeGetTypeID(attribute) + +Gets the type id of the attribute. +""" +function mlirAttributeGetTypeID(attribute) + @ccall (MLIR_C_PATH[]).mlirAttributeGetTypeID(attribute::MlirAttribute)::MlirTypeID +end + +""" + mlirAttributeGetDialect(attribute) + +Gets the dialect of the attribute. +""" +function mlirAttributeGetDialect(attribute) + @ccall (MLIR_C_PATH[]).mlirAttributeGetDialect(attribute::MlirAttribute)::MlirDialect +end + +""" + mlirAttributeIsNull(attr) + +Checks whether an attribute is null. +""" +function mlirAttributeIsNull(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsNull(attr::MlirAttribute)::Bool +end + +""" + mlirAttributeEqual(a1, a2) + +Checks if two attributes are equal. +""" +function mlirAttributeEqual(a1, a2) + @ccall (MLIR_C_PATH[]).mlirAttributeEqual(a1::MlirAttribute, a2::MlirAttribute)::Bool +end + +""" + mlirAttributePrint(attr, callback, userData) + +Prints an attribute by sending chunks of the string representation and forwarding `userData to `callback`. Note that the callback may be called several times with consecutive chunks of the string. +""" +function mlirAttributePrint(attr, callback, userData) + @ccall (MLIR_C_PATH[]).mlirAttributePrint( + attr::MlirAttribute, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirAttributeDump(attr) + +Prints the attribute to the standard error stream. +""" +function mlirAttributeDump(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeDump(attr::MlirAttribute)::Cvoid +end + +""" + mlirNamedAttributeGet(name, attr) + +Associates an attribute with the name. Takes ownership of neither. +""" +function mlirNamedAttributeGet(name, attr) + @ccall (MLIR_C_PATH[]).mlirNamedAttributeGet( + name::MlirIdentifier, attr::MlirAttribute + )::MlirNamedAttribute +end + +""" + mlirIdentifierGet(context, str) + +Gets an identifier with the given string value. +""" +function mlirIdentifierGet(context, str) + @ccall (MLIR_C_PATH[]).mlirIdentifierGet( + context::MlirContext, str::MlirStringRef + )::MlirIdentifier +end + +""" + mlirIdentifierGetContext(arg1) + +Returns the context associated with this identifier +""" +function mlirIdentifierGetContext(arg1) + @ccall (MLIR_C_PATH[]).mlirIdentifierGetContext(arg1::MlirIdentifier)::MlirContext +end + +""" + mlirIdentifierEqual(ident, other) + +Checks whether two identifiers are the same. +""" +function mlirIdentifierEqual(ident, other) + @ccall (MLIR_C_PATH[]).mlirIdentifierEqual( + ident::MlirIdentifier, other::MlirIdentifier + )::Bool +end + +""" + mlirIdentifierStr(ident) + +Gets the string value of the identifier. +""" +function mlirIdentifierStr(ident) + @ccall (MLIR_C_PATH[]).mlirIdentifierStr(ident::MlirIdentifier)::MlirStringRef +end + +""" + mlirSymbolTableGetSymbolAttributeName() + +Returns the name of the attribute used to store symbol names compatible with symbol tables. +""" +function mlirSymbolTableGetSymbolAttributeName() + @ccall (MLIR_C_PATH[]).mlirSymbolTableGetSymbolAttributeName()::MlirStringRef +end + +""" + mlirSymbolTableGetVisibilityAttributeName() + +Returns the name of the attribute used to store symbol visibility. +""" +function mlirSymbolTableGetVisibilityAttributeName() + @ccall (MLIR_C_PATH[]).mlirSymbolTableGetVisibilityAttributeName()::MlirStringRef +end + +""" + mlirSymbolTableCreate(operation) + +Creates a symbol table for the given operation. If the operation does not have the SymbolTable trait, returns a null symbol table. +""" +function mlirSymbolTableCreate(operation) + @ccall (MLIR_C_PATH[]).mlirSymbolTableCreate(operation::MlirOperation)::MlirSymbolTable +end + +""" + mlirSymbolTableIsNull(symbolTable) + +Returns true if the symbol table is null. +""" +function mlirSymbolTableIsNull(symbolTable) + @ccall (MLIR_C_PATH[]).mlirSymbolTableIsNull(symbolTable::MlirSymbolTable)::Bool +end + +""" + mlirSymbolTableDestroy(symbolTable) + +Destroys the symbol table created with [`mlirSymbolTableCreate`](@ref). This does not affect the operations in the table. +""" +function mlirSymbolTableDestroy(symbolTable) + @ccall (MLIR_C_PATH[]).mlirSymbolTableDestroy(symbolTable::MlirSymbolTable)::Cvoid +end + +""" + mlirSymbolTableLookup(symbolTable, name) + +Looks up a symbol with the given name in the given symbol table and returns the operation that corresponds to the symbol. If the symbol cannot be found, returns a null operation. +""" +function mlirSymbolTableLookup(symbolTable, name) + @ccall (MLIR_C_PATH[]).mlirSymbolTableLookup( + symbolTable::MlirSymbolTable, name::MlirStringRef + )::MlirOperation +end + +""" + mlirSymbolTableInsert(symbolTable, operation) + +Inserts the given operation into the given symbol table. The operation must have the symbol trait. If the symbol table already has a symbol with the same name, renames the symbol being inserted to ensure name uniqueness. Note that this does not move the operation itself into the block of the symbol table operation, this should be done separately. Returns the name of the symbol after insertion. +""" +function mlirSymbolTableInsert(symbolTable, operation) + @ccall (MLIR_C_PATH[]).mlirSymbolTableInsert( + symbolTable::MlirSymbolTable, operation::MlirOperation + )::MlirAttribute +end + +""" + mlirSymbolTableErase(symbolTable, operation) + +Removes the given operation from the symbol table and erases it. +""" +function mlirSymbolTableErase(symbolTable, operation) + @ccall (MLIR_C_PATH[]).mlirSymbolTableErase( + symbolTable::MlirSymbolTable, operation::MlirOperation + )::Cvoid +end + +""" + mlirSymbolTableReplaceAllSymbolUses(oldSymbol, newSymbol, from) + +Attempt to replace all uses that are nested within the given operation of the given symbol 'oldSymbol' with the provided 'newSymbol'. This does not traverse into nested symbol tables. Will fail atomically if there are any unknown operations that may be potential symbol tables. +""" +function mlirSymbolTableReplaceAllSymbolUses(oldSymbol, newSymbol, from) + @ccall (MLIR_C_PATH[]).mlirSymbolTableReplaceAllSymbolUses( + oldSymbol::MlirStringRef, newSymbol::MlirStringRef, from::MlirOperation + )::MlirLogicalResult +end + +""" + mlirSymbolTableWalkSymbolTables(from, allSymUsesVisible, callback, userData) + +Walks all symbol table operations nested within, and including, `op`. For each symbol table operation, the provided callback is invoked with the op and a boolean signifying if the symbols within that symbol table can be treated as if all uses within the IR are visible to the caller. `allSymUsesVisible` identifies whether all of the symbol uses of symbols within `op` are visible. +""" +function mlirSymbolTableWalkSymbolTables(from, allSymUsesVisible, callback, userData) + @ccall (MLIR_C_PATH[]).mlirSymbolTableWalkSymbolTables( + from::MlirOperation, + allSymUsesVisible::Bool, + callback::Ptr{Cvoid}, + userData::Ptr{Cvoid}, + )::Cvoid +end + +""" + mlirAffineExprGetContext(affineExpr) + +Gets the context that owns the affine expression. +""" +function mlirAffineExprGetContext(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprGetContext(affineExpr::MlirAffineExpr)::MlirContext +end + +""" + mlirAffineExprEqual(lhs, rhs) + +Returns `true` if the two affine expressions are equal. +""" +function mlirAffineExprEqual(lhs, rhs) + @ccall (MLIR_C_PATH[]).mlirAffineExprEqual( + lhs::MlirAffineExpr, rhs::MlirAffineExpr + )::Bool +end + +""" + mlirAffineExprIsNull(affineExpr) + +Returns `true` if the given affine expression is a null expression. Note constant zero is not a null expression. +""" +function mlirAffineExprIsNull(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsNull(affineExpr::MlirAffineExpr)::Bool +end + +""" + mlirAffineExprPrint(affineExpr, callback, userData) + +Prints an affine expression by sending chunks of the string representation and forwarding `userData to `callback`. Note that the callback may be called several times with consecutive chunks of the string. +""" +function mlirAffineExprPrint(affineExpr, callback, userData) + @ccall (MLIR_C_PATH[]).mlirAffineExprPrint( + affineExpr::MlirAffineExpr, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirAffineExprDump(affineExpr) + +Prints the affine expression to the standard error stream. +""" +function mlirAffineExprDump(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprDump(affineExpr::MlirAffineExpr)::Cvoid +end + +""" + mlirAffineExprIsSymbolicOrConstant(affineExpr) + +Checks whether the given affine expression is made out of only symbols and constants. +""" +function mlirAffineExprIsSymbolicOrConstant(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsSymbolicOrConstant( + affineExpr::MlirAffineExpr + )::Bool +end + +""" + mlirAffineExprIsPureAffine(affineExpr) + +Checks whether the given affine expression is a pure affine expression, i.e. mul, floordiv, ceildic, and mod is only allowed w.r.t constants. +""" +function mlirAffineExprIsPureAffine(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsPureAffine(affineExpr::MlirAffineExpr)::Bool +end + +""" + mlirAffineExprGetLargestKnownDivisor(affineExpr) + +Returns the greatest known integral divisor of this affine expression. The result is always positive. +""" +function mlirAffineExprGetLargestKnownDivisor(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprGetLargestKnownDivisor( + affineExpr::MlirAffineExpr + )::Int64 +end + +""" + mlirAffineExprIsMultipleOf(affineExpr, factor) + +Checks whether the given affine expression is a multiple of 'factor'. +""" +function mlirAffineExprIsMultipleOf(affineExpr, factor) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsMultipleOf( + affineExpr::MlirAffineExpr, factor::Int64 + )::Bool +end + +""" + mlirAffineExprIsFunctionOfDim(affineExpr, position) + +Checks whether the given affine expression involves AffineDimExpr 'position'. +""" +function mlirAffineExprIsFunctionOfDim(affineExpr, position) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsFunctionOfDim( + affineExpr::MlirAffineExpr, position::intptr_t + )::Bool +end + +""" + mlirAffineExprCompose(affineExpr, affineMap) + +Composes the given map with the given expression. +""" +function mlirAffineExprCompose(affineExpr, affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineExprCompose( + affineExpr::MlirAffineExpr, affineMap::MlirAffineMap + )::MlirAffineExpr +end + +""" + mlirAffineExprIsADim(affineExpr) + +Checks whether the given affine expression is a dimension expression. +""" +function mlirAffineExprIsADim(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsADim(affineExpr::MlirAffineExpr)::Bool +end + +""" + mlirAffineDimExprGet(ctx, position) + +Creates an affine dimension expression with 'position' in the context. +""" +function mlirAffineDimExprGet(ctx, position) + @ccall (MLIR_C_PATH[]).mlirAffineDimExprGet( + ctx::MlirContext, position::intptr_t + )::MlirAffineExpr +end + +""" + mlirAffineDimExprGetPosition(affineExpr) + +Returns the position of the given affine dimension expression. +""" +function mlirAffineDimExprGetPosition(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineDimExprGetPosition( + affineExpr::MlirAffineExpr + )::intptr_t +end + +""" + mlirAffineExprIsASymbol(affineExpr) + +Checks whether the given affine expression is a symbol expression. +""" +function mlirAffineExprIsASymbol(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsASymbol(affineExpr::MlirAffineExpr)::Bool +end + +""" + mlirAffineSymbolExprGet(ctx, position) + +Creates an affine symbol expression with 'position' in the context. +""" +function mlirAffineSymbolExprGet(ctx, position) + @ccall (MLIR_C_PATH[]).mlirAffineSymbolExprGet( + ctx::MlirContext, position::intptr_t + )::MlirAffineExpr +end + +""" + mlirAffineSymbolExprGetPosition(affineExpr) + +Returns the position of the given affine symbol expression. +""" +function mlirAffineSymbolExprGetPosition(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineSymbolExprGetPosition( + affineExpr::MlirAffineExpr + )::intptr_t +end + +""" + mlirAffineExprIsAConstant(affineExpr) + +Checks whether the given affine expression is a constant expression. +""" +function mlirAffineExprIsAConstant(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsAConstant(affineExpr::MlirAffineExpr)::Bool +end + +""" + mlirAffineConstantExprGet(ctx, constant) + +Creates an affine constant expression with 'constant' in the context. +""" +function mlirAffineConstantExprGet(ctx, constant) + @ccall (MLIR_C_PATH[]).mlirAffineConstantExprGet( + ctx::MlirContext, constant::Int64 + )::MlirAffineExpr +end + +""" + mlirAffineConstantExprGetValue(affineExpr) + +Returns the value of the given affine constant expression. +""" +function mlirAffineConstantExprGetValue(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineConstantExprGetValue(affineExpr::MlirAffineExpr)::Int64 +end + +""" + mlirAffineExprIsAAdd(affineExpr) + +Checks whether the given affine expression is an add expression. +""" +function mlirAffineExprIsAAdd(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsAAdd(affineExpr::MlirAffineExpr)::Bool +end + +""" + mlirAffineAddExprGet(lhs, rhs) + +Creates an affine add expression with 'lhs' and 'rhs'. +""" +function mlirAffineAddExprGet(lhs, rhs) + @ccall (MLIR_C_PATH[]).mlirAffineAddExprGet( + lhs::MlirAffineExpr, rhs::MlirAffineExpr + )::MlirAffineExpr +end + +""" + mlirAffineExprIsAMul(affineExpr) + +Checks whether the given affine expression is an mul expression. +""" +function mlirAffineExprIsAMul(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsAMul(affineExpr::MlirAffineExpr)::Bool +end + +""" + mlirAffineMulExprGet(lhs, rhs) + +Creates an affine mul expression with 'lhs' and 'rhs'. +""" +function mlirAffineMulExprGet(lhs, rhs) + @ccall (MLIR_C_PATH[]).mlirAffineMulExprGet( + lhs::MlirAffineExpr, rhs::MlirAffineExpr + )::MlirAffineExpr +end + +""" + mlirAffineExprIsAMod(affineExpr) + +Checks whether the given affine expression is an mod expression. +""" +function mlirAffineExprIsAMod(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsAMod(affineExpr::MlirAffineExpr)::Bool +end + +""" + mlirAffineModExprGet(lhs, rhs) + +Creates an affine mod expression with 'lhs' and 'rhs'. +""" +function mlirAffineModExprGet(lhs, rhs) + @ccall (MLIR_C_PATH[]).mlirAffineModExprGet( + lhs::MlirAffineExpr, rhs::MlirAffineExpr + )::MlirAffineExpr +end + +""" + mlirAffineExprIsAFloorDiv(affineExpr) + +Checks whether the given affine expression is an floordiv expression. +""" +function mlirAffineExprIsAFloorDiv(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsAFloorDiv(affineExpr::MlirAffineExpr)::Bool +end + +""" + mlirAffineFloorDivExprGet(lhs, rhs) + +Creates an affine floordiv expression with 'lhs' and 'rhs'. +""" +function mlirAffineFloorDivExprGet(lhs, rhs) + @ccall (MLIR_C_PATH[]).mlirAffineFloorDivExprGet( + lhs::MlirAffineExpr, rhs::MlirAffineExpr + )::MlirAffineExpr +end + +""" + mlirAffineExprIsACeilDiv(affineExpr) + +Checks whether the given affine expression is an ceildiv expression. +""" +function mlirAffineExprIsACeilDiv(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsACeilDiv(affineExpr::MlirAffineExpr)::Bool +end + +""" + mlirAffineCeilDivExprGet(lhs, rhs) + +Creates an affine ceildiv expression with 'lhs' and 'rhs'. +""" +function mlirAffineCeilDivExprGet(lhs, rhs) + @ccall (MLIR_C_PATH[]).mlirAffineCeilDivExprGet( + lhs::MlirAffineExpr, rhs::MlirAffineExpr + )::MlirAffineExpr +end + +""" + mlirAffineExprIsABinary(affineExpr) + +Checks whether the given affine expression is binary. +""" +function mlirAffineExprIsABinary(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineExprIsABinary(affineExpr::MlirAffineExpr)::Bool +end + +""" + mlirAffineBinaryOpExprGetLHS(affineExpr) + +Returns the left hand side affine expression of the given affine binary operation expression. +""" +function mlirAffineBinaryOpExprGetLHS(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineBinaryOpExprGetLHS( + affineExpr::MlirAffineExpr + )::MlirAffineExpr +end + +""" + mlirAffineBinaryOpExprGetRHS(affineExpr) + +Returns the right hand side affine expression of the given affine binary operation expression. +""" +function mlirAffineBinaryOpExprGetRHS(affineExpr) + @ccall (MLIR_C_PATH[]).mlirAffineBinaryOpExprGetRHS( + affineExpr::MlirAffineExpr + )::MlirAffineExpr +end + +""" + mlirAffineMapGetContext(affineMap) + +Gets the context that the given affine map was created with +""" +function mlirAffineMapGetContext(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapGetContext(affineMap::MlirAffineMap)::MlirContext +end + +""" + mlirAffineMapIsNull(affineMap) + +Checks whether an affine map is null. +""" +function mlirAffineMapIsNull(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapIsNull(affineMap::MlirAffineMap)::Bool +end + +""" + mlirAffineMapEqual(a1, a2) + +Checks if two affine maps are equal. +""" +function mlirAffineMapEqual(a1, a2) + @ccall (MLIR_C_PATH[]).mlirAffineMapEqual(a1::MlirAffineMap, a2::MlirAffineMap)::Bool +end + +""" + mlirAffineMapPrint(affineMap, callback, userData) + +Prints an affine map by sending chunks of the string representation and forwarding `userData to `callback`. Note that the callback may be called several times with consecutive chunks of the string. +""" +function mlirAffineMapPrint(affineMap, callback, userData) + @ccall (MLIR_C_PATH[]).mlirAffineMapPrint( + affineMap::MlirAffineMap, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirAffineMapDump(affineMap) + +Prints the affine map to the standard error stream. +""" +function mlirAffineMapDump(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapDump(affineMap::MlirAffineMap)::Cvoid +end + +""" + mlirAffineMapEmptyGet(ctx) + +Creates a zero result affine map with no dimensions or symbols in the context. The affine map is owned by the context. +""" +function mlirAffineMapEmptyGet(ctx) + @ccall (MLIR_C_PATH[]).mlirAffineMapEmptyGet(ctx::MlirContext)::MlirAffineMap +end + +""" + mlirAffineMapZeroResultGet(ctx, dimCount, symbolCount) + +Creates a zero result affine map of the given dimensions and symbols in the context. The affine map is owned by the context. +""" +function mlirAffineMapZeroResultGet(ctx, dimCount, symbolCount) + @ccall (MLIR_C_PATH[]).mlirAffineMapZeroResultGet( + ctx::MlirContext, dimCount::intptr_t, symbolCount::intptr_t + )::MlirAffineMap +end + +""" + mlirAffineMapGet(ctx, dimCount, symbolCount, nAffineExprs, affineExprs) + +Creates an affine map with results defined by the given list of affine expressions. The map resulting map also has the requested number of input dimensions and symbols, regardless of them being used in the results. +""" +function mlirAffineMapGet(ctx, dimCount, symbolCount, nAffineExprs, affineExprs) + @ccall (MLIR_C_PATH[]).mlirAffineMapGet( + ctx::MlirContext, + dimCount::intptr_t, + symbolCount::intptr_t, + nAffineExprs::intptr_t, + affineExprs::Ptr{MlirAffineExpr}, + )::MlirAffineMap +end + +""" + mlirAffineMapConstantGet(ctx, val) + +Creates a single constant result affine map in the context. The affine map is owned by the context. +""" +function mlirAffineMapConstantGet(ctx, val) + @ccall (MLIR_C_PATH[]).mlirAffineMapConstantGet( + ctx::MlirContext, val::Int64 + )::MlirAffineMap +end + +""" + mlirAffineMapMultiDimIdentityGet(ctx, numDims) + +Creates an affine map with 'numDims' identity in the context. The affine map is owned by the context. +""" +function mlirAffineMapMultiDimIdentityGet(ctx, numDims) + @ccall (MLIR_C_PATH[]).mlirAffineMapMultiDimIdentityGet( + ctx::MlirContext, numDims::intptr_t + )::MlirAffineMap +end + +""" + mlirAffineMapMinorIdentityGet(ctx, dims, results) + +Creates an identity affine map on the most minor dimensions in the context. The affine map is owned by the context. The function asserts that the number of dimensions is greater or equal to the number of results. +""" +function mlirAffineMapMinorIdentityGet(ctx, dims, results) + @ccall (MLIR_C_PATH[]).mlirAffineMapMinorIdentityGet( + ctx::MlirContext, dims::intptr_t, results::intptr_t + )::MlirAffineMap +end + +""" + mlirAffineMapPermutationGet(ctx, size, permutation) + +Creates an affine map with a permutation expression and its size in the context. The permutation expression is a non-empty vector of integers. The elements of the permutation vector must be continuous from 0 and cannot be repeated (i.e. `[1,2,0]` is a valid permutation. `[2,0]` or `[1,1,2]` is an invalid permutation.) The affine map is owned by the context. +""" +function mlirAffineMapPermutationGet(ctx, size, permutation) + @ccall (MLIR_C_PATH[]).mlirAffineMapPermutationGet( + ctx::MlirContext, size::intptr_t, permutation::Ptr{Cuint} + )::MlirAffineMap +end + +""" + mlirAffineMapIsIdentity(affineMap) + +Checks whether the given affine map is an identity affine map. The function asserts that the number of dimensions is greater or equal to the number of results. +""" +function mlirAffineMapIsIdentity(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapIsIdentity(affineMap::MlirAffineMap)::Bool +end + +""" + mlirAffineMapIsMinorIdentity(affineMap) + +Checks whether the given affine map is a minor identity affine map. +""" +function mlirAffineMapIsMinorIdentity(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapIsMinorIdentity(affineMap::MlirAffineMap)::Bool +end + +""" + mlirAffineMapIsEmpty(affineMap) + +Checks whether the given affine map is an empty affine map. +""" +function mlirAffineMapIsEmpty(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapIsEmpty(affineMap::MlirAffineMap)::Bool +end + +""" + mlirAffineMapIsSingleConstant(affineMap) + +Checks whether the given affine map is a single result constant affine map. +""" +function mlirAffineMapIsSingleConstant(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapIsSingleConstant(affineMap::MlirAffineMap)::Bool +end + +""" + mlirAffineMapGetSingleConstantResult(affineMap) + +Returns the constant result of the given affine map. The function asserts that the map has a single constant result. +""" +function mlirAffineMapGetSingleConstantResult(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapGetSingleConstantResult( + affineMap::MlirAffineMap + )::Int64 +end + +""" + mlirAffineMapGetNumDims(affineMap) + +Returns the number of dimensions of the given affine map. +""" +function mlirAffineMapGetNumDims(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapGetNumDims(affineMap::MlirAffineMap)::intptr_t +end + +""" + mlirAffineMapGetNumSymbols(affineMap) + +Returns the number of symbols of the given affine map. +""" +function mlirAffineMapGetNumSymbols(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapGetNumSymbols(affineMap::MlirAffineMap)::intptr_t +end + +""" + mlirAffineMapGetNumResults(affineMap) + +Returns the number of results of the given affine map. +""" +function mlirAffineMapGetNumResults(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapGetNumResults(affineMap::MlirAffineMap)::intptr_t +end + +""" + mlirAffineMapGetResult(affineMap, pos) + +Returns the result at the given position. +""" +function mlirAffineMapGetResult(affineMap, pos) + @ccall (MLIR_C_PATH[]).mlirAffineMapGetResult( + affineMap::MlirAffineMap, pos::intptr_t + )::MlirAffineExpr +end + +""" + mlirAffineMapGetNumInputs(affineMap) + +Returns the number of inputs (dimensions + symbols) of the given affine map. +""" +function mlirAffineMapGetNumInputs(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapGetNumInputs(affineMap::MlirAffineMap)::intptr_t +end + +""" + mlirAffineMapIsProjectedPermutation(affineMap) + +Checks whether the given affine map represents a subset of a symbol-less permutation map. +""" +function mlirAffineMapIsProjectedPermutation(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapIsProjectedPermutation( + affineMap::MlirAffineMap + )::Bool +end + +""" + mlirAffineMapIsPermutation(affineMap) + +Checks whether the given affine map represents a symbol-less permutation map. +""" +function mlirAffineMapIsPermutation(affineMap) + @ccall (MLIR_C_PATH[]).mlirAffineMapIsPermutation(affineMap::MlirAffineMap)::Bool +end + +""" + mlirAffineMapGetSubMap(affineMap, size, resultPos) + +Returns the affine map consisting of the `resultPos` subset. +""" +function mlirAffineMapGetSubMap(affineMap, size, resultPos) + @ccall (MLIR_C_PATH[]).mlirAffineMapGetSubMap( + affineMap::MlirAffineMap, size::intptr_t, resultPos::Ptr{intptr_t} + )::MlirAffineMap +end + +""" + mlirAffineMapGetMajorSubMap(affineMap, numResults) + +Returns the affine map consisting of the most major `numResults` results. Returns the null AffineMap if the `numResults` is equal to zero. Returns the `affineMap` if `numResults` is greater or equals to number of results of the given affine map. +""" +function mlirAffineMapGetMajorSubMap(affineMap, numResults) + @ccall (MLIR_C_PATH[]).mlirAffineMapGetMajorSubMap( + affineMap::MlirAffineMap, numResults::intptr_t + )::MlirAffineMap +end + +""" + mlirAffineMapGetMinorSubMap(affineMap, numResults) + +Returns the affine map consisting of the most minor `numResults` results. Returns the null AffineMap if the `numResults` is equal to zero. Returns the `affineMap` if `numResults` is greater or equals to number of results of the given affine map. +""" +function mlirAffineMapGetMinorSubMap(affineMap, numResults) + @ccall (MLIR_C_PATH[]).mlirAffineMapGetMinorSubMap( + affineMap::MlirAffineMap, numResults::intptr_t + )::MlirAffineMap +end + +""" + mlirAffineMapReplace(affineMap, expression, replacement, numResultDims, numResultSyms) + +Apply AffineExpr::replace(`map`) to each of the results and return a new new AffineMap with the new results and the specified number of dims and symbols. +""" +function mlirAffineMapReplace( + affineMap, expression, replacement, numResultDims, numResultSyms +) + @ccall (MLIR_C_PATH[]).mlirAffineMapReplace( + affineMap::MlirAffineMap, + expression::MlirAffineExpr, + replacement::MlirAffineExpr, + numResultDims::intptr_t, + numResultSyms::intptr_t, + )::MlirAffineMap +end + +""" + mlirAffineMapCompressUnusedSymbols(affineMaps, size, result, populateResult) + +Returns the simplified affine map resulting from dropping the symbols that do not appear in any of the individual maps in `affineMaps`. Asserts that all maps in `affineMaps` are normalized to the same number of dims and symbols. Takes a callback `populateResult` to fill the `res` container with value `m` at entry `idx`. This allows returning without worrying about ownership considerations. +""" +function mlirAffineMapCompressUnusedSymbols(affineMaps, size, result, populateResult) + @ccall (MLIR_C_PATH[]).mlirAffineMapCompressUnusedSymbols( + affineMaps::Ptr{MlirAffineMap}, + size::intptr_t, + result::Ptr{Cvoid}, + populateResult::Ptr{Cvoid}, + )::Cvoid +end + +""" + mlirAttributeGetNull() + +Returns an empty attribute. +""" +function mlirAttributeGetNull() + @ccall (MLIR_C_PATH[]).mlirAttributeGetNull()::MlirAttribute +end + +function mlirAttributeIsALocation(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsALocation(attr::MlirAttribute)::Bool +end + +""" + mlirAttributeIsAAffineMap(attr) + +Checks whether the given attribute is an affine map attribute. +""" +function mlirAttributeIsAAffineMap(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAAffineMap(attr::MlirAttribute)::Bool +end + +""" + mlirAffineMapAttrGet(map) + +Creates an affine map attribute wrapping the given map. The attribute belongs to the same context as the affine map. +""" +function mlirAffineMapAttrGet(map) + @ccall (MLIR_C_PATH[]).mlirAffineMapAttrGet(map::MlirAffineMap)::MlirAttribute +end + +""" + mlirAffineMapAttrGetValue(attr) + +Returns the affine map wrapped in the given affine map attribute. +""" +function mlirAffineMapAttrGetValue(attr) + @ccall (MLIR_C_PATH[]).mlirAffineMapAttrGetValue(attr::MlirAttribute)::MlirAffineMap +end + +""" + mlirAffineMapAttrGetTypeID() + +Returns the typeID of an AffineMap attribute. +""" +function mlirAffineMapAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirAffineMapAttrGetTypeID()::MlirTypeID +end + +""" + mlirAttributeIsAArray(attr) + +Checks whether the given attribute is an array attribute. +""" +function mlirAttributeIsAArray(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAArray(attr::MlirAttribute)::Bool +end + +""" + mlirArrayAttrGet(ctx, numElements, elements) + +Creates an array element containing the given list of elements in the given context. +""" +function mlirArrayAttrGet(ctx, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirArrayAttrGet( + ctx::MlirContext, numElements::intptr_t, elements::Ptr{MlirAttribute} + )::MlirAttribute +end + +""" + mlirArrayAttrGetNumElements(attr) + +Returns the number of elements stored in the given array attribute. +""" +function mlirArrayAttrGetNumElements(attr) + @ccall (MLIR_C_PATH[]).mlirArrayAttrGetNumElements(attr::MlirAttribute)::intptr_t +end + +""" + mlirArrayAttrGetElement(attr, pos) + +Returns pos-th element stored in the given array attribute. +""" +function mlirArrayAttrGetElement(attr, pos) + @ccall (MLIR_C_PATH[]).mlirArrayAttrGetElement( + attr::MlirAttribute, pos::intptr_t + )::MlirAttribute +end + +""" + mlirArrayAttrGetTypeID() + +Returns the typeID of an Array attribute. +""" +function mlirArrayAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirArrayAttrGetTypeID()::MlirTypeID +end + +""" + mlirAttributeIsADictionary(attr) + +Checks whether the given attribute is a dictionary attribute. +""" +function mlirAttributeIsADictionary(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADictionary(attr::MlirAttribute)::Bool +end + +""" + mlirDictionaryAttrGet(ctx, numElements, elements) + +Creates a dictionary attribute containing the given list of elements in the provided context. +""" +function mlirDictionaryAttrGet(ctx, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDictionaryAttrGet( + ctx::MlirContext, numElements::intptr_t, elements::Ptr{MlirNamedAttribute} + )::MlirAttribute +end + +""" + mlirDictionaryAttrGetNumElements(attr) + +Returns the number of attributes contained in a dictionary attribute. +""" +function mlirDictionaryAttrGetNumElements(attr) + @ccall (MLIR_C_PATH[]).mlirDictionaryAttrGetNumElements(attr::MlirAttribute)::intptr_t +end + +""" + mlirDictionaryAttrGetElement(attr, pos) + +Returns pos-th element of the given dictionary attribute. +""" +function mlirDictionaryAttrGetElement(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDictionaryAttrGetElement( + attr::MlirAttribute, pos::intptr_t + )::MlirNamedAttribute +end + +""" + mlirDictionaryAttrGetElementByName(attr, name) + +Returns the dictionary attribute element with the given name or NULL if the given name does not exist in the dictionary. +""" +function mlirDictionaryAttrGetElementByName(attr, name) + @ccall (MLIR_C_PATH[]).mlirDictionaryAttrGetElementByName( + attr::MlirAttribute, name::MlirStringRef + )::MlirAttribute +end + +""" + mlirDictionaryAttrGetTypeID() + +Returns the typeID of a Dictionary attribute. +""" +function mlirDictionaryAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirDictionaryAttrGetTypeID()::MlirTypeID +end + +""" + mlirAttributeIsAFloat(attr) + +Checks whether the given attribute is a floating point attribute. +""" +function mlirAttributeIsAFloat(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAFloat(attr::MlirAttribute)::Bool +end + +""" + mlirFloatAttrDoubleGet(ctx, type, value) + +Creates a floating point attribute in the given context with the given double value and double-precision FP semantics. +""" +function mlirFloatAttrDoubleGet(ctx, type, value) + @ccall (MLIR_C_PATH[]).mlirFloatAttrDoubleGet( + ctx::MlirContext, type::MlirType, value::Cdouble + )::MlirAttribute +end + +""" + mlirFloatAttrDoubleGetChecked(loc, type, value) + +Same as "[`mlirFloatAttrDoubleGet`](@ref)", but if the type is not valid for a construction of a FloatAttr, returns a null [`MlirAttribute`](@ref). +""" +function mlirFloatAttrDoubleGetChecked(loc, type, value) + @ccall (MLIR_C_PATH[]).mlirFloatAttrDoubleGetChecked( + loc::MlirLocation, type::MlirType, value::Cdouble + )::MlirAttribute +end + +""" + mlirFloatAttrGetValueDouble(attr) + +Returns the value stored in the given floating point attribute, interpreting the value as double. +""" +function mlirFloatAttrGetValueDouble(attr) + @ccall (MLIR_C_PATH[]).mlirFloatAttrGetValueDouble(attr::MlirAttribute)::Cdouble +end + +""" + mlirFloatAttrGetTypeID() + +Returns the typeID of a Float attribute. +""" +function mlirFloatAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFloatAttrGetTypeID()::MlirTypeID +end + +""" + mlirAttributeIsAInteger(attr) + +Checks whether the given attribute is an integer attribute. +""" +function mlirAttributeIsAInteger(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAInteger(attr::MlirAttribute)::Bool +end + +""" + mlirIntegerAttrGet(type, value) + +Creates an integer attribute of the given type with the given integer value. +""" +function mlirIntegerAttrGet(type, value) + @ccall (MLIR_C_PATH[]).mlirIntegerAttrGet(type::MlirType, value::Int64)::MlirAttribute +end + +""" + mlirIntegerAttrGetValueInt(attr) + +Returns the value stored in the given integer attribute, assuming the value is of signless type and fits into a signed 64-bit integer. +""" +function mlirIntegerAttrGetValueInt(attr) + @ccall (MLIR_C_PATH[]).mlirIntegerAttrGetValueInt(attr::MlirAttribute)::Int64 +end + +""" + mlirIntegerAttrGetValueSInt(attr) + +Returns the value stored in the given integer attribute, assuming the value is of signed type and fits into a signed 64-bit integer. +""" +function mlirIntegerAttrGetValueSInt(attr) + @ccall (MLIR_C_PATH[]).mlirIntegerAttrGetValueSInt(attr::MlirAttribute)::Int64 +end + +""" + mlirIntegerAttrGetValueUInt(attr) + +Returns the value stored in the given integer attribute, assuming the value is of unsigned type and fits into an unsigned 64-bit integer. +""" +function mlirIntegerAttrGetValueUInt(attr) + @ccall (MLIR_C_PATH[]).mlirIntegerAttrGetValueUInt(attr::MlirAttribute)::UInt64 +end + +""" + mlirIntegerAttrGetTypeID() + +Returns the typeID of an Integer attribute. +""" +function mlirIntegerAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirIntegerAttrGetTypeID()::MlirTypeID +end + +""" + mlirAttributeIsABool(attr) + +Checks whether the given attribute is a bool attribute. +""" +function mlirAttributeIsABool(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsABool(attr::MlirAttribute)::Bool +end + +""" + mlirBoolAttrGet(ctx, value) + +Creates a bool attribute in the given context with the given value. +""" +function mlirBoolAttrGet(ctx, value) + @ccall (MLIR_C_PATH[]).mlirBoolAttrGet(ctx::MlirContext, value::Cint)::MlirAttribute +end + +""" + mlirBoolAttrGetValue(attr) + +Returns the value stored in the given bool attribute. +""" +function mlirBoolAttrGetValue(attr) + @ccall (MLIR_C_PATH[]).mlirBoolAttrGetValue(attr::MlirAttribute)::Bool +end + +""" + mlirAttributeIsAIntegerSet(attr) + +Checks whether the given attribute is an integer set attribute. +""" +function mlirAttributeIsAIntegerSet(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAIntegerSet(attr::MlirAttribute)::Bool +end + +""" + mlirIntegerSetAttrGetTypeID() + +Returns the typeID of an IntegerSet attribute. +""" +function mlirIntegerSetAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirIntegerSetAttrGetTypeID()::MlirTypeID +end + +""" + mlirAttributeIsAOpaque(attr) + +Checks whether the given attribute is an opaque attribute. +""" +function mlirAttributeIsAOpaque(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAOpaque(attr::MlirAttribute)::Bool +end + +""" + mlirOpaqueAttrGet(ctx, dialectNamespace, dataLength, data, type) + +Creates an opaque attribute in the given context associated with the dialect identified by its namespace. The attribute contains opaque byte data of the specified length (data need not be null-terminated). +""" +function mlirOpaqueAttrGet(ctx, dialectNamespace, dataLength, data, type) + @ccall (MLIR_C_PATH[]).mlirOpaqueAttrGet( + ctx::MlirContext, + dialectNamespace::MlirStringRef, + dataLength::intptr_t, + data::Cstring, + type::MlirType, + )::MlirAttribute +end + +""" + mlirOpaqueAttrGetDialectNamespace(attr) + +Returns the namespace of the dialect with which the given opaque attribute is associated. The namespace string is owned by the context. +""" +function mlirOpaqueAttrGetDialectNamespace(attr) + @ccall (MLIR_C_PATH[]).mlirOpaqueAttrGetDialectNamespace( + attr::MlirAttribute + )::MlirStringRef +end + +""" + mlirOpaqueAttrGetData(attr) + +Returns the raw data as a string reference. The data remains live as long as the context in which the attribute lives. +""" +function mlirOpaqueAttrGetData(attr) + @ccall (MLIR_C_PATH[]).mlirOpaqueAttrGetData(attr::MlirAttribute)::MlirStringRef +end + +""" + mlirOpaqueAttrGetTypeID() + +Returns the typeID of an Opaque attribute. +""" +function mlirOpaqueAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirOpaqueAttrGetTypeID()::MlirTypeID +end + +""" + mlirAttributeIsAString(attr) + +Checks whether the given attribute is a string attribute. +""" +function mlirAttributeIsAString(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAString(attr::MlirAttribute)::Bool +end + +""" + mlirStringAttrGet(ctx, str) + +Creates a string attribute in the given context containing the given string. +""" +function mlirStringAttrGet(ctx, str) + @ccall (MLIR_C_PATH[]).mlirStringAttrGet( + ctx::MlirContext, str::MlirStringRef + )::MlirAttribute +end + +""" + mlirStringAttrTypedGet(type, str) + +Creates a string attribute in the given context containing the given string. Additionally, the attribute has the given type. +""" +function mlirStringAttrTypedGet(type, str) + @ccall (MLIR_C_PATH[]).mlirStringAttrTypedGet( + type::MlirType, str::MlirStringRef + )::MlirAttribute +end + +""" + mlirStringAttrGetValue(attr) + +Returns the attribute values as a string reference. The data remains live as long as the context in which the attribute lives. +""" +function mlirStringAttrGetValue(attr) + @ccall (MLIR_C_PATH[]).mlirStringAttrGetValue(attr::MlirAttribute)::MlirStringRef +end + +""" + mlirStringAttrGetTypeID() + +Returns the typeID of a String attribute. +""" +function mlirStringAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirStringAttrGetTypeID()::MlirTypeID +end + +""" + mlirAttributeIsASymbolRef(attr) + +Checks whether the given attribute is a symbol reference attribute. +""" +function mlirAttributeIsASymbolRef(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsASymbolRef(attr::MlirAttribute)::Bool +end + +""" + mlirSymbolRefAttrGet(ctx, symbol, numReferences, references) + +Creates a symbol reference attribute in the given context referencing a symbol identified by the given string inside a list of nested references. Each of the references in the list must not be nested. +""" +function mlirSymbolRefAttrGet(ctx, symbol, numReferences, references) + @ccall (MLIR_C_PATH[]).mlirSymbolRefAttrGet( + ctx::MlirContext, + symbol::MlirStringRef, + numReferences::intptr_t, + references::Ptr{MlirAttribute}, + )::MlirAttribute +end + +""" + mlirSymbolRefAttrGetRootReference(attr) + +Returns the string reference to the root referenced symbol. The data remains live as long as the context in which the attribute lives. +""" +function mlirSymbolRefAttrGetRootReference(attr) + @ccall (MLIR_C_PATH[]).mlirSymbolRefAttrGetRootReference( + attr::MlirAttribute + )::MlirStringRef +end + +""" + mlirSymbolRefAttrGetLeafReference(attr) + +Returns the string reference to the leaf referenced symbol. The data remains live as long as the context in which the attribute lives. +""" +function mlirSymbolRefAttrGetLeafReference(attr) + @ccall (MLIR_C_PATH[]).mlirSymbolRefAttrGetLeafReference( + attr::MlirAttribute + )::MlirStringRef +end + +""" + mlirSymbolRefAttrGetNumNestedReferences(attr) + +Returns the number of references nested in the given symbol reference attribute. +""" +function mlirSymbolRefAttrGetNumNestedReferences(attr) + @ccall (MLIR_C_PATH[]).mlirSymbolRefAttrGetNumNestedReferences( + attr::MlirAttribute + )::intptr_t +end + +""" + mlirSymbolRefAttrGetNestedReference(attr, pos) + +Returns pos-th reference nested in the given symbol reference attribute. +""" +function mlirSymbolRefAttrGetNestedReference(attr, pos) + @ccall (MLIR_C_PATH[]).mlirSymbolRefAttrGetNestedReference( + attr::MlirAttribute, pos::intptr_t + )::MlirAttribute +end + +""" + mlirSymbolRefAttrGetTypeID() + +Returns the typeID of an SymbolRef attribute. +""" +function mlirSymbolRefAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirSymbolRefAttrGetTypeID()::MlirTypeID +end + +""" + mlirDisctinctAttrCreate(referencedAttr) + +Creates a DisctinctAttr with the referenced attribute. +""" +function mlirDisctinctAttrCreate(referencedAttr) + @ccall (MLIR_C_PATH[]).mlirDisctinctAttrCreate( + referencedAttr::MlirAttribute + )::MlirAttribute +end + +""" + mlirAttributeIsAFlatSymbolRef(attr) + +Checks whether the given attribute is a flat symbol reference attribute. +""" +function mlirAttributeIsAFlatSymbolRef(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAFlatSymbolRef(attr::MlirAttribute)::Bool +end + +""" + mlirFlatSymbolRefAttrGet(ctx, symbol) + +Creates a flat symbol reference attribute in the given context referencing a symbol identified by the given string. +""" +function mlirFlatSymbolRefAttrGet(ctx, symbol) + @ccall (MLIR_C_PATH[]).mlirFlatSymbolRefAttrGet( + ctx::MlirContext, symbol::MlirStringRef + )::MlirAttribute +end + +""" + mlirFlatSymbolRefAttrGetValue(attr) + +Returns the referenced symbol as a string reference. The data remains live as long as the context in which the attribute lives. +""" +function mlirFlatSymbolRefAttrGetValue(attr) + @ccall (MLIR_C_PATH[]).mlirFlatSymbolRefAttrGetValue(attr::MlirAttribute)::MlirStringRef +end + +""" + mlirAttributeIsAType(attr) + +Checks whether the given attribute is a type attribute. +""" +function mlirAttributeIsAType(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAType(attr::MlirAttribute)::Bool +end + +""" + mlirTypeAttrGet(type) + +Creates a type attribute wrapping the given type in the same context as the type. +""" +function mlirTypeAttrGet(type) + @ccall (MLIR_C_PATH[]).mlirTypeAttrGet(type::MlirType)::MlirAttribute +end + +""" + mlirTypeAttrGetValue(attr) + +Returns the type stored in the given type attribute. +""" +function mlirTypeAttrGetValue(attr) + @ccall (MLIR_C_PATH[]).mlirTypeAttrGetValue(attr::MlirAttribute)::MlirType +end + +""" + mlirTypeAttrGetTypeID() + +Returns the typeID of a Type attribute. +""" +function mlirTypeAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirTypeAttrGetTypeID()::MlirTypeID +end + +""" + mlirAttributeIsAUnit(attr) + +Checks whether the given attribute is a unit attribute. +""" +function mlirAttributeIsAUnit(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAUnit(attr::MlirAttribute)::Bool +end + +""" + mlirUnitAttrGet(ctx) + +Creates a unit attribute in the given context. +""" +function mlirUnitAttrGet(ctx) + @ccall (MLIR_C_PATH[]).mlirUnitAttrGet(ctx::MlirContext)::MlirAttribute +end + +""" + mlirUnitAttrGetTypeID() + +Returns the typeID of a Unit attribute. +""" +function mlirUnitAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirUnitAttrGetTypeID()::MlirTypeID +end + +""" + mlirAttributeIsAElements(attr) + +Checks whether the given attribute is an elements attribute. +""" +function mlirAttributeIsAElements(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAElements(attr::MlirAttribute)::Bool +end + +""" + mlirElementsAttrGetValue(attr, rank, idxs) + +Returns the element at the given rank-dimensional index. +""" +function mlirElementsAttrGetValue(attr, rank, idxs) + @ccall (MLIR_C_PATH[]).mlirElementsAttrGetValue( + attr::MlirAttribute, rank::intptr_t, idxs::Ptr{UInt64} + )::MlirAttribute +end + +""" + mlirElementsAttrIsValidIndex(attr, rank, idxs) + +Checks whether the given rank-dimensional index is valid in the given elements attribute. +""" +function mlirElementsAttrIsValidIndex(attr, rank, idxs) + @ccall (MLIR_C_PATH[]).mlirElementsAttrIsValidIndex( + attr::MlirAttribute, rank::intptr_t, idxs::Ptr{UInt64} + )::Bool +end + +""" + mlirElementsAttrGetNumElements(attr) + +Gets the total number of elements in the given elements attribute. In order to iterate over the attribute, obtain its type, which must be a statically shaped type and use its sizes to build a multi-dimensional index. +""" +function mlirElementsAttrGetNumElements(attr) + @ccall (MLIR_C_PATH[]).mlirElementsAttrGetNumElements(attr::MlirAttribute)::Int64 +end + +function mlirDenseArrayAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirDenseArrayAttrGetTypeID()::MlirTypeID +end + +""" + mlirAttributeIsADenseBoolArray(attr) + +Checks whether the given attribute is a dense array attribute. +""" +function mlirAttributeIsADenseBoolArray(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADenseBoolArray(attr::MlirAttribute)::Bool +end + +function mlirAttributeIsADenseI8Array(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADenseI8Array(attr::MlirAttribute)::Bool +end + +function mlirAttributeIsADenseI16Array(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADenseI16Array(attr::MlirAttribute)::Bool +end + +function mlirAttributeIsADenseI32Array(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADenseI32Array(attr::MlirAttribute)::Bool +end + +function mlirAttributeIsADenseI64Array(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADenseI64Array(attr::MlirAttribute)::Bool +end + +function mlirAttributeIsADenseF32Array(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADenseF32Array(attr::MlirAttribute)::Bool +end + +function mlirAttributeIsADenseF64Array(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADenseF64Array(attr::MlirAttribute)::Bool +end + +""" + mlirDenseBoolArrayGet(ctx, size, values) + +Create a dense array attribute with the given elements. +""" +function mlirDenseBoolArrayGet(ctx, size, values) + @ccall (MLIR_C_PATH[]).mlirDenseBoolArrayGet( + ctx::MlirContext, size::intptr_t, values::Ptr{Cint} + )::MlirAttribute +end + +function mlirDenseI8ArrayGet(ctx, size, values) + @ccall (MLIR_C_PATH[]).mlirDenseI8ArrayGet( + ctx::MlirContext, size::intptr_t, values::Ptr{Int8} + )::MlirAttribute +end + +function mlirDenseI16ArrayGet(ctx, size, values) + @ccall (MLIR_C_PATH[]).mlirDenseI16ArrayGet( + ctx::MlirContext, size::intptr_t, values::Ptr{Int16} + )::MlirAttribute +end + +function mlirDenseI32ArrayGet(ctx, size, values) + @ccall (MLIR_C_PATH[]).mlirDenseI32ArrayGet( + ctx::MlirContext, size::intptr_t, values::Ptr{Int32} + )::MlirAttribute +end + +function mlirDenseI64ArrayGet(ctx, size, values) + @ccall (MLIR_C_PATH[]).mlirDenseI64ArrayGet( + ctx::MlirContext, size::intptr_t, values::Ptr{Int64} + )::MlirAttribute +end + +function mlirDenseF32ArrayGet(ctx, size, values) + @ccall (MLIR_C_PATH[]).mlirDenseF32ArrayGet( + ctx::MlirContext, size::intptr_t, values::Ptr{Cfloat} + )::MlirAttribute +end + +function mlirDenseF64ArrayGet(ctx, size, values) + @ccall (MLIR_C_PATH[]).mlirDenseF64ArrayGet( + ctx::MlirContext, size::intptr_t, values::Ptr{Cdouble} + )::MlirAttribute +end + +""" + mlirDenseArrayGetNumElements(attr) + +Get the size of a dense array. +""" +function mlirDenseArrayGetNumElements(attr) + @ccall (MLIR_C_PATH[]).mlirDenseArrayGetNumElements(attr::MlirAttribute)::intptr_t +end + +""" + mlirDenseBoolArrayGetElement(attr, pos) + +Get an element of a dense array. +""" +function mlirDenseBoolArrayGetElement(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseBoolArrayGetElement( + attr::MlirAttribute, pos::intptr_t + )::Bool +end + +function mlirDenseI8ArrayGetElement(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseI8ArrayGetElement( + attr::MlirAttribute, pos::intptr_t + )::Int8 +end + +function mlirDenseI16ArrayGetElement(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseI16ArrayGetElement( + attr::MlirAttribute, pos::intptr_t + )::Int16 +end + +function mlirDenseI32ArrayGetElement(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseI32ArrayGetElement( + attr::MlirAttribute, pos::intptr_t + )::Int32 +end + +function mlirDenseI64ArrayGetElement(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseI64ArrayGetElement( + attr::MlirAttribute, pos::intptr_t + )::Int64 +end + +function mlirDenseF32ArrayGetElement(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseF32ArrayGetElement( + attr::MlirAttribute, pos::intptr_t + )::Cfloat +end + +function mlirDenseF64ArrayGetElement(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseF64ArrayGetElement( + attr::MlirAttribute, pos::intptr_t + )::Cdouble +end + +""" + mlirAttributeIsADenseElements(attr) + +Checks whether the given attribute is a dense elements attribute. +""" +function mlirAttributeIsADenseElements(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADenseElements(attr::MlirAttribute)::Bool +end + +function mlirAttributeIsADenseIntElements(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADenseIntElements(attr::MlirAttribute)::Bool +end + +function mlirAttributeIsADenseFPElements(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADenseFPElements(attr::MlirAttribute)::Bool +end + +""" + mlirDenseIntOrFPElementsAttrGetTypeID() + +Returns the typeID of an DenseIntOrFPElements attribute. +""" +function mlirDenseIntOrFPElementsAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirDenseIntOrFPElementsAttrGetTypeID()::MlirTypeID +end + +""" + mlirDenseElementsAttrGet(shapedType, numElements, elements) + +Creates a dense elements attribute with the given Shaped type and elements in the same context as the type. +""" +function mlirDenseElementsAttrGet(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGet( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{MlirAttribute} + )::MlirAttribute +end + +""" + mlirDenseElementsAttrRawBufferGet(shapedType, rawBufferSize, rawBuffer) + +Creates a dense elements attribute with the given Shaped type and elements populated from a packed, row-major opaque buffer of contents. + +The format of the raw buffer is a densely packed array of values that can be bitcast to the storage format of the element type specified. Types that are not byte aligned will be: - For bitwidth > 1: Rounded up to the next byte. - For bitwidth = 1: Packed into 8bit bytes with bits corresponding to the linear order of the shape type from MSB to LSB, padded to on the right. + +A raw buffer of a single element (or for 1-bit, a byte of value 0 or 255) will be interpreted as a splat. User code should be prepared for additional, conformant patterns to be identified as splats in the future. +""" +function mlirDenseElementsAttrRawBufferGet(shapedType, rawBufferSize, rawBuffer) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrRawBufferGet( + shapedType::MlirType, rawBufferSize::Csize_t, rawBuffer::Ptr{Cvoid} + )::MlirAttribute +end + +""" + mlirDenseElementsAttrSplatGet(shapedType, element) + +Creates a dense elements attribute with the given Shaped type containing a single replicated element (splat). +""" +function mlirDenseElementsAttrSplatGet(shapedType, element) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrSplatGet( + shapedType::MlirType, element::MlirAttribute + )::MlirAttribute +end + +function mlirDenseElementsAttrBoolSplatGet(shapedType, element) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrBoolSplatGet( + shapedType::MlirType, element::Bool + )::MlirAttribute +end + +function mlirDenseElementsAttrUInt8SplatGet(shapedType, element) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrUInt8SplatGet( + shapedType::MlirType, element::UInt8 + )::MlirAttribute +end + +function mlirDenseElementsAttrInt8SplatGet(shapedType, element) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrInt8SplatGet( + shapedType::MlirType, element::Int8 + )::MlirAttribute +end + +function mlirDenseElementsAttrUInt32SplatGet(shapedType, element) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrUInt32SplatGet( + shapedType::MlirType, element::UInt32 + )::MlirAttribute +end + +function mlirDenseElementsAttrInt32SplatGet(shapedType, element) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrInt32SplatGet( + shapedType::MlirType, element::Int32 + )::MlirAttribute +end + +function mlirDenseElementsAttrUInt64SplatGet(shapedType, element) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrUInt64SplatGet( + shapedType::MlirType, element::UInt64 + )::MlirAttribute +end + +function mlirDenseElementsAttrInt64SplatGet(shapedType, element) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrInt64SplatGet( + shapedType::MlirType, element::Int64 + )::MlirAttribute +end + +function mlirDenseElementsAttrFloatSplatGet(shapedType, element) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrFloatSplatGet( + shapedType::MlirType, element::Cfloat + )::MlirAttribute +end + +function mlirDenseElementsAttrDoubleSplatGet(shapedType, element) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrDoubleSplatGet( + shapedType::MlirType, element::Cdouble + )::MlirAttribute +end + +""" + mlirDenseElementsAttrBoolGet(shapedType, numElements, elements) + +Creates a dense elements attribute with the given shaped type from elements of a specific type. Expects the element type of the shaped type to match the data element type. +""" +function mlirDenseElementsAttrBoolGet(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrBoolGet( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{Cint} + )::MlirAttribute +end + +function mlirDenseElementsAttrUInt8Get(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrUInt8Get( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{UInt8} + )::MlirAttribute +end + +function mlirDenseElementsAttrInt8Get(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrInt8Get( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{Int8} + )::MlirAttribute +end + +function mlirDenseElementsAttrUInt16Get(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrUInt16Get( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{UInt16} + )::MlirAttribute +end + +function mlirDenseElementsAttrInt16Get(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrInt16Get( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{Int16} + )::MlirAttribute +end + +function mlirDenseElementsAttrUInt32Get(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrUInt32Get( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{UInt32} + )::MlirAttribute +end + +function mlirDenseElementsAttrInt32Get(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrInt32Get( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{Int32} + )::MlirAttribute +end + +function mlirDenseElementsAttrUInt64Get(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrUInt64Get( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{UInt64} + )::MlirAttribute +end + +function mlirDenseElementsAttrInt64Get(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrInt64Get( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{Int64} + )::MlirAttribute +end + +function mlirDenseElementsAttrFloatGet(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrFloatGet( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{Cfloat} + )::MlirAttribute +end + +function mlirDenseElementsAttrDoubleGet(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrDoubleGet( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{Cdouble} + )::MlirAttribute +end + +function mlirDenseElementsAttrBFloat16Get(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrBFloat16Get( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{UInt16} + )::MlirAttribute +end + +function mlirDenseElementsAttrFloat16Get(shapedType, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrFloat16Get( + shapedType::MlirType, numElements::intptr_t, elements::Ptr{UInt16} + )::MlirAttribute +end + +""" + mlirDenseElementsAttrStringGet(shapedType, numElements, strs) + +Creates a dense elements attribute with the given shaped type from string elements. +""" +function mlirDenseElementsAttrStringGet(shapedType, numElements, strs) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrStringGet( + shapedType::MlirType, numElements::intptr_t, strs::Ptr{MlirStringRef} + )::MlirAttribute +end + +""" + mlirDenseElementsAttrReshapeGet(attr, shapedType) + +Creates a dense elements attribute that has the same data as the given dense elements attribute and a different shaped type. The new type must have the same total number of elements. +""" +function mlirDenseElementsAttrReshapeGet(attr, shapedType) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrReshapeGet( + attr::MlirAttribute, shapedType::MlirType + )::MlirAttribute +end + +""" + mlirDenseElementsAttrIsSplat(attr) + +Checks whether the given dense elements attribute contains a single replicated value (splat). +""" +function mlirDenseElementsAttrIsSplat(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrIsSplat(attr::MlirAttribute)::Bool +end + +""" + mlirDenseElementsAttrGetSplatValue(attr) + +Returns the single replicated value (splat) of a specific type contained by the given dense elements attribute. +""" +function mlirDenseElementsAttrGetSplatValue(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetSplatValue( + attr::MlirAttribute + )::MlirAttribute +end + +function mlirDenseElementsAttrGetBoolSplatValue(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetBoolSplatValue(attr::MlirAttribute)::Cint +end + +function mlirDenseElementsAttrGetInt8SplatValue(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetInt8SplatValue(attr::MlirAttribute)::Int8 +end + +function mlirDenseElementsAttrGetUInt8SplatValue(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetUInt8SplatValue( + attr::MlirAttribute + )::UInt8 +end + +function mlirDenseElementsAttrGetInt32SplatValue(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetInt32SplatValue( + attr::MlirAttribute + )::Int32 +end + +function mlirDenseElementsAttrGetUInt32SplatValue(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetUInt32SplatValue( + attr::MlirAttribute + )::UInt32 +end + +function mlirDenseElementsAttrGetInt64SplatValue(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetInt64SplatValue( + attr::MlirAttribute + )::Int64 +end + +function mlirDenseElementsAttrGetUInt64SplatValue(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetUInt64SplatValue( + attr::MlirAttribute + )::UInt64 +end + +function mlirDenseElementsAttrGetFloatSplatValue(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetFloatSplatValue( + attr::MlirAttribute + )::Cfloat +end + +function mlirDenseElementsAttrGetDoubleSplatValue(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetDoubleSplatValue( + attr::MlirAttribute + )::Cdouble +end + +function mlirDenseElementsAttrGetStringSplatValue(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetStringSplatValue( + attr::MlirAttribute + )::MlirStringRef +end + +""" + mlirDenseElementsAttrGetBoolValue(attr, pos) + +Returns the pos-th value (flat contiguous indexing) of a specific type contained by the given dense elements attribute. +""" +function mlirDenseElementsAttrGetBoolValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetBoolValue( + attr::MlirAttribute, pos::intptr_t + )::Bool +end + +function mlirDenseElementsAttrGetInt8Value(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetInt8Value( + attr::MlirAttribute, pos::intptr_t + )::Int8 +end + +function mlirDenseElementsAttrGetUInt8Value(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetUInt8Value( + attr::MlirAttribute, pos::intptr_t + )::UInt8 +end + +function mlirDenseElementsAttrGetInt16Value(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetInt16Value( + attr::MlirAttribute, pos::intptr_t + )::Int16 +end + +function mlirDenseElementsAttrGetUInt16Value(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetUInt16Value( + attr::MlirAttribute, pos::intptr_t + )::UInt16 +end + +function mlirDenseElementsAttrGetInt32Value(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetInt32Value( + attr::MlirAttribute, pos::intptr_t + )::Int32 +end + +function mlirDenseElementsAttrGetUInt32Value(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetUInt32Value( + attr::MlirAttribute, pos::intptr_t + )::UInt32 +end + +function mlirDenseElementsAttrGetInt64Value(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetInt64Value( + attr::MlirAttribute, pos::intptr_t + )::Int64 +end + +function mlirDenseElementsAttrGetUInt64Value(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetUInt64Value( + attr::MlirAttribute, pos::intptr_t + )::UInt64 +end + +function mlirDenseElementsAttrGetFloatValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetFloatValue( + attr::MlirAttribute, pos::intptr_t + )::Cfloat +end + +function mlirDenseElementsAttrGetDoubleValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetDoubleValue( + attr::MlirAttribute, pos::intptr_t + )::Cdouble +end + +function mlirDenseElementsAttrGetStringValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetStringValue( + attr::MlirAttribute, pos::intptr_t + )::MlirStringRef +end + +""" + mlirDenseElementsAttrGetRawData(attr) + +Returns the raw data of the given dense elements attribute. +""" +function mlirDenseElementsAttrGetRawData(attr) + @ccall (MLIR_C_PATH[]).mlirDenseElementsAttrGetRawData(attr::MlirAttribute)::Ptr{Cvoid} +end + +function mlirAttributeIsADenseResourceElements(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsADenseResourceElements(attr::MlirAttribute)::Bool +end + +""" + mlirUnmanagedDenseResourceElementsAttrGet(shapedType, name, data, dataLength, dataAlignment, dataIsMutable, deleter, userData) + +Unlike the typed accessors below, constructs the attribute with a raw data buffer and no type/alignment checking. Use a more strongly typed accessor if possible. If dataIsMutable is false, then an immutable AsmResourceBlob will be created and that passed data contents will be treated as const. If the deleter is non NULL, then it will be called when the data buffer can no longer be accessed (passing userData to it). +""" +function mlirUnmanagedDenseResourceElementsAttrGet( + shapedType, name, data, dataLength, dataAlignment, dataIsMutable, deleter, userData +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + data::Ptr{Cvoid}, + dataLength::Csize_t, + dataAlignment::Csize_t, + dataIsMutable::Bool, + deleter::Ptr{Cvoid}, + userData::Ptr{Cvoid}, + )::MlirAttribute +end + +function mlirUnmanagedDenseBoolResourceElementsAttrGet( + shapedType, name, numElements, elements +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseBoolResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + numElements::intptr_t, + elements::Ptr{Cint}, + )::MlirAttribute +end + +function mlirUnmanagedDenseUInt8ResourceElementsAttrGet( + shapedType, name, numElements, elements +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseUInt8ResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + numElements::intptr_t, + elements::Ptr{UInt8}, + )::MlirAttribute +end + +function mlirUnmanagedDenseInt8ResourceElementsAttrGet( + shapedType, name, numElements, elements +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseInt8ResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + numElements::intptr_t, + elements::Ptr{Int8}, + )::MlirAttribute +end + +function mlirUnmanagedDenseUInt16ResourceElementsAttrGet( + shapedType, name, numElements, elements +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseUInt16ResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + numElements::intptr_t, + elements::Ptr{UInt16}, + )::MlirAttribute +end + +function mlirUnmanagedDenseInt16ResourceElementsAttrGet( + shapedType, name, numElements, elements +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseInt16ResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + numElements::intptr_t, + elements::Ptr{Int16}, + )::MlirAttribute +end + +function mlirUnmanagedDenseUInt32ResourceElementsAttrGet( + shapedType, name, numElements, elements +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseUInt32ResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + numElements::intptr_t, + elements::Ptr{UInt32}, + )::MlirAttribute +end + +function mlirUnmanagedDenseInt32ResourceElementsAttrGet( + shapedType, name, numElements, elements +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseInt32ResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + numElements::intptr_t, + elements::Ptr{Int32}, + )::MlirAttribute +end + +function mlirUnmanagedDenseUInt64ResourceElementsAttrGet( + shapedType, name, numElements, elements +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseUInt64ResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + numElements::intptr_t, + elements::Ptr{UInt64}, + )::MlirAttribute +end + +function mlirUnmanagedDenseInt64ResourceElementsAttrGet( + shapedType, name, numElements, elements +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseInt64ResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + numElements::intptr_t, + elements::Ptr{Int64}, + )::MlirAttribute +end + +function mlirUnmanagedDenseFloatResourceElementsAttrGet( + shapedType, name, numElements, elements +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseFloatResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + numElements::intptr_t, + elements::Ptr{Cfloat}, + )::MlirAttribute +end + +function mlirUnmanagedDenseDoubleResourceElementsAttrGet( + shapedType, name, numElements, elements +) + @ccall (MLIR_C_PATH[]).mlirUnmanagedDenseDoubleResourceElementsAttrGet( + shapedType::MlirType, + name::MlirStringRef, + numElements::intptr_t, + elements::Ptr{Cdouble}, + )::MlirAttribute +end + +""" + mlirDenseBoolResourceElementsAttrGetValue(attr, pos) + +Returns the pos-th value (flat contiguous indexing) of a specific type contained by the given dense resource elements attribute. +""" +function mlirDenseBoolResourceElementsAttrGetValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseBoolResourceElementsAttrGetValue( + attr::MlirAttribute, pos::intptr_t + )::Bool +end + +function mlirDenseInt8ResourceElementsAttrGetValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseInt8ResourceElementsAttrGetValue( + attr::MlirAttribute, pos::intptr_t + )::Int8 +end + +function mlirDenseUInt8ResourceElementsAttrGetValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseUInt8ResourceElementsAttrGetValue( + attr::MlirAttribute, pos::intptr_t + )::UInt8 +end + +function mlirDenseInt16ResourceElementsAttrGetValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseInt16ResourceElementsAttrGetValue( + attr::MlirAttribute, pos::intptr_t + )::Int16 +end + +function mlirDenseUInt16ResourceElementsAttrGetValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseUInt16ResourceElementsAttrGetValue( + attr::MlirAttribute, pos::intptr_t + )::UInt16 +end + +function mlirDenseInt32ResourceElementsAttrGetValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseInt32ResourceElementsAttrGetValue( + attr::MlirAttribute, pos::intptr_t + )::Int32 +end + +function mlirDenseUInt32ResourceElementsAttrGetValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseUInt32ResourceElementsAttrGetValue( + attr::MlirAttribute, pos::intptr_t + )::UInt32 +end + +function mlirDenseInt64ResourceElementsAttrGetValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseInt64ResourceElementsAttrGetValue( + attr::MlirAttribute, pos::intptr_t + )::Int64 +end + +function mlirDenseUInt64ResourceElementsAttrGetValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseUInt64ResourceElementsAttrGetValue( + attr::MlirAttribute, pos::intptr_t + )::UInt64 +end + +function mlirDenseFloatResourceElementsAttrGetValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseFloatResourceElementsAttrGetValue( + attr::MlirAttribute, pos::intptr_t + )::Cfloat +end + +function mlirDenseDoubleResourceElementsAttrGetValue(attr, pos) + @ccall (MLIR_C_PATH[]).mlirDenseDoubleResourceElementsAttrGetValue( + attr::MlirAttribute, pos::intptr_t + )::Cdouble +end + +""" + mlirAttributeIsASparseElements(attr) + +Checks whether the given attribute is a sparse elements attribute. +""" +function mlirAttributeIsASparseElements(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsASparseElements(attr::MlirAttribute)::Bool +end + +""" + mlirSparseElementsAttribute(shapedType, denseIndices, denseValues) + +Creates a sparse elements attribute of the given shape from a list of indices and a list of associated values. Both lists are expected to be dense elements attributes with the same number of elements. The list of indices is expected to contain 64-bit integers. The attribute is created in the same context as the type. +""" +function mlirSparseElementsAttribute(shapedType, denseIndices, denseValues) + @ccall (MLIR_C_PATH[]).mlirSparseElementsAttribute( + shapedType::MlirType, denseIndices::MlirAttribute, denseValues::MlirAttribute + )::MlirAttribute +end + +""" + mlirSparseElementsAttrGetIndices(attr) + +Returns the dense elements attribute containing 64-bit integer indices of non-null elements in the given sparse elements attribute. +""" +function mlirSparseElementsAttrGetIndices(attr) + @ccall (MLIR_C_PATH[]).mlirSparseElementsAttrGetIndices( + attr::MlirAttribute + )::MlirAttribute +end + +""" + mlirSparseElementsAttrGetValues(attr) + +Returns the dense elements attribute containing the non-null elements in the given sparse elements attribute. +""" +function mlirSparseElementsAttrGetValues(attr) + @ccall (MLIR_C_PATH[]).mlirSparseElementsAttrGetValues( + attr::MlirAttribute + )::MlirAttribute +end + +""" + mlirSparseElementsAttrGetTypeID() + +Returns the typeID of a SparseElements attribute. +""" +function mlirSparseElementsAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirSparseElementsAttrGetTypeID()::MlirTypeID +end + +function mlirAttributeIsAStridedLayout(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAStridedLayout(attr::MlirAttribute)::Bool +end + +function mlirStridedLayoutAttrGet(ctx, offset, numStrides, strides) + @ccall (MLIR_C_PATH[]).mlirStridedLayoutAttrGet( + ctx::MlirContext, offset::Int64, numStrides::intptr_t, strides::Ptr{Int64} + )::MlirAttribute +end + +function mlirStridedLayoutAttrGetOffset(attr) + @ccall (MLIR_C_PATH[]).mlirStridedLayoutAttrGetOffset(attr::MlirAttribute)::Int64 +end + +function mlirStridedLayoutAttrGetNumStrides(attr) + @ccall (MLIR_C_PATH[]).mlirStridedLayoutAttrGetNumStrides(attr::MlirAttribute)::intptr_t +end + +function mlirStridedLayoutAttrGetStride(attr, pos) + @ccall (MLIR_C_PATH[]).mlirStridedLayoutAttrGetStride( + attr::MlirAttribute, pos::intptr_t + )::Int64 +end + +""" + mlirStridedLayoutAttrGetTypeID() + +Returns the typeID of a StridedLayout attribute. +""" +function mlirStridedLayoutAttrGetTypeID() + @ccall (MLIR_C_PATH[]).mlirStridedLayoutAttrGetTypeID()::MlirTypeID +end + +""" + mlirIntegerTypeGetTypeID() + +Returns the typeID of an Integer type. +""" +function mlirIntegerTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirIntegerTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAInteger(type) + +Checks whether the given type is an integer type. +""" +function mlirTypeIsAInteger(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAInteger(type::MlirType)::Bool +end + +""" + mlirIntegerTypeGet(ctx, bitwidth) + +Creates a signless integer type of the given bitwidth in the context. The type is owned by the context. +""" +function mlirIntegerTypeGet(ctx, bitwidth) + @ccall (MLIR_C_PATH[]).mlirIntegerTypeGet(ctx::MlirContext, bitwidth::Cuint)::MlirType +end + +""" + mlirIntegerTypeSignedGet(ctx, bitwidth) + +Creates a signed integer type of the given bitwidth in the context. The type is owned by the context. +""" +function mlirIntegerTypeSignedGet(ctx, bitwidth) + @ccall (MLIR_C_PATH[]).mlirIntegerTypeSignedGet( + ctx::MlirContext, bitwidth::Cuint + )::MlirType +end + +""" + mlirIntegerTypeUnsignedGet(ctx, bitwidth) + +Creates an unsigned integer type of the given bitwidth in the context. The type is owned by the context. +""" +function mlirIntegerTypeUnsignedGet(ctx, bitwidth) + @ccall (MLIR_C_PATH[]).mlirIntegerTypeUnsignedGet( + ctx::MlirContext, bitwidth::Cuint + )::MlirType +end + +""" + mlirIntegerTypeGetWidth(type) + +Returns the bitwidth of an integer type. +""" +function mlirIntegerTypeGetWidth(type) + @ccall (MLIR_C_PATH[]).mlirIntegerTypeGetWidth(type::MlirType)::Cuint +end + +""" + mlirIntegerTypeIsSignless(type) + +Checks whether the given integer type is signless. +""" +function mlirIntegerTypeIsSignless(type) + @ccall (MLIR_C_PATH[]).mlirIntegerTypeIsSignless(type::MlirType)::Bool +end + +""" + mlirIntegerTypeIsSigned(type) + +Checks whether the given integer type is signed. +""" +function mlirIntegerTypeIsSigned(type) + @ccall (MLIR_C_PATH[]).mlirIntegerTypeIsSigned(type::MlirType)::Bool +end + +""" + mlirIntegerTypeIsUnsigned(type) + +Checks whether the given integer type is unsigned. +""" +function mlirIntegerTypeIsUnsigned(type) + @ccall (MLIR_C_PATH[]).mlirIntegerTypeIsUnsigned(type::MlirType)::Bool +end + +""" + mlirIndexTypeGetTypeID() + +Returns the typeID of an Index type. +""" +function mlirIndexTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirIndexTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAIndex(type) + +Checks whether the given type is an index type. +""" +function mlirTypeIsAIndex(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAIndex(type::MlirType)::Bool +end + +""" + mlirIndexTypeGet(ctx) + +Creates an index type in the given context. The type is owned by the context. +""" +function mlirIndexTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirIndexTypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirTypeIsAFloat(type) + +Checks whether the given type is a floating-point type. +""" +function mlirTypeIsAFloat(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAFloat(type::MlirType)::Bool +end + +""" + mlirFloatTypeGetWidth(type) + +Returns the bitwidth of a floating-point type. +""" +function mlirFloatTypeGetWidth(type) + @ccall (MLIR_C_PATH[]).mlirFloatTypeGetWidth(type::MlirType)::Cuint +end + +""" + mlirFloat8E5M2TypeGetTypeID() + +Returns the typeID of an Float8E5M2 type. +""" +function mlirFloat8E5M2TypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFloat8E5M2TypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAFloat8E5M2(type) + +Checks whether the given type is an f8E5M2 type. +""" +function mlirTypeIsAFloat8E5M2(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAFloat8E5M2(type::MlirType)::Bool +end + +""" + mlirFloat8E5M2TypeGet(ctx) + +Creates an f8E5M2 type in the given context. The type is owned by the context. +""" +function mlirFloat8E5M2TypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirFloat8E5M2TypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirFloat8E4M3TypeGetTypeID() + +Returns the typeID of an Float8E4M3 type. +""" +function mlirFloat8E4M3TypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFloat8E4M3TypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAFloat8E4M3(type) + +Checks whether the given type is an f8E4M3 type. +""" +function mlirTypeIsAFloat8E4M3(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAFloat8E4M3(type::MlirType)::Bool +end + +""" + mlirFloat8E4M3TypeGet(ctx) + +Creates an f8E4M3 type in the given context. The type is owned by the context. +""" +function mlirFloat8E4M3TypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirFloat8E4M3TypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirFloat8E4M3FNTypeGetTypeID() + +Returns the typeID of an Float8E4M3FN type. +""" +function mlirFloat8E4M3FNTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFloat8E4M3FNTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAFloat8E4M3FN(type) + +Checks whether the given type is an f8E4M3FN type. +""" +function mlirTypeIsAFloat8E4M3FN(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAFloat8E4M3FN(type::MlirType)::Bool +end + +""" + mlirFloat8E4M3FNTypeGet(ctx) + +Creates an f8E4M3FN type in the given context. The type is owned by the context. +""" +function mlirFloat8E4M3FNTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirFloat8E4M3FNTypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirFloat8E5M2FNUZTypeGetTypeID() + +Returns the typeID of an Float8E5M2FNUZ type. +""" +function mlirFloat8E5M2FNUZTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFloat8E5M2FNUZTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAFloat8E5M2FNUZ(type) + +Checks whether the given type is an f8E5M2FNUZ type. +""" +function mlirTypeIsAFloat8E5M2FNUZ(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAFloat8E5M2FNUZ(type::MlirType)::Bool +end + +""" + mlirFloat8E5M2FNUZTypeGet(ctx) + +Creates an f8E5M2FNUZ type in the given context. The type is owned by the context. +""" +function mlirFloat8E5M2FNUZTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirFloat8E5M2FNUZTypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirFloat8E4M3FNUZTypeGetTypeID() + +Returns the typeID of an Float8E4M3FNUZ type. +""" +function mlirFloat8E4M3FNUZTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFloat8E4M3FNUZTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAFloat8E4M3FNUZ(type) + +Checks whether the given type is an f8E4M3FNUZ type. +""" +function mlirTypeIsAFloat8E4M3FNUZ(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAFloat8E4M3FNUZ(type::MlirType)::Bool +end + +""" + mlirFloat8E4M3FNUZTypeGet(ctx) + +Creates an f8E4M3FNUZ type in the given context. The type is owned by the context. +""" +function mlirFloat8E4M3FNUZTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirFloat8E4M3FNUZTypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirFloat8E4M3B11FNUZTypeGetTypeID() + +Returns the typeID of an Float8E4M3B11FNUZ type. +""" +function mlirFloat8E4M3B11FNUZTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFloat8E4M3B11FNUZTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAFloat8E4M3B11FNUZ(type) + +Checks whether the given type is an f8E4M3B11FNUZ type. +""" +function mlirTypeIsAFloat8E4M3B11FNUZ(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAFloat8E4M3B11FNUZ(type::MlirType)::Bool +end + +""" + mlirFloat8E4M3B11FNUZTypeGet(ctx) + +Creates an f8E4M3B11FNUZ type in the given context. The type is owned by the context. +""" +function mlirFloat8E4M3B11FNUZTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirFloat8E4M3B11FNUZTypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirBFloat16TypeGetTypeID() + +Returns the typeID of an BFloat16 type. +""" +function mlirBFloat16TypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirBFloat16TypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsABF16(type) + +Checks whether the given type is a bf16 type. +""" +function mlirTypeIsABF16(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsABF16(type::MlirType)::Bool +end + +""" + mlirBF16TypeGet(ctx) + +Creates a bf16 type in the given context. The type is owned by the context. +""" +function mlirBF16TypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirBF16TypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirFloat16TypeGetTypeID() + +Returns the typeID of an Float16 type. +""" +function mlirFloat16TypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFloat16TypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAF16(type) + +Checks whether the given type is an f16 type. +""" +function mlirTypeIsAF16(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAF16(type::MlirType)::Bool +end + +""" + mlirF16TypeGet(ctx) + +Creates an f16 type in the given context. The type is owned by the context. +""" +function mlirF16TypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirF16TypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirFloat32TypeGetTypeID() + +Returns the typeID of an Float32 type. +""" +function mlirFloat32TypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFloat32TypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAF32(type) + +Checks whether the given type is an f32 type. +""" +function mlirTypeIsAF32(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAF32(type::MlirType)::Bool +end + +""" + mlirF32TypeGet(ctx) + +Creates an f32 type in the given context. The type is owned by the context. +""" +function mlirF32TypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirF32TypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirFloat64TypeGetTypeID() + +Returns the typeID of an Float64 type. +""" +function mlirFloat64TypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFloat64TypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAF64(type) + +Checks whether the given type is an f64 type. +""" +function mlirTypeIsAF64(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAF64(type::MlirType)::Bool +end + +""" + mlirF64TypeGet(ctx) + +Creates a f64 type in the given context. The type is owned by the context. +""" +function mlirF64TypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirF64TypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirFloatTF32TypeGetTypeID() + +Returns the typeID of a TF32 type. +""" +function mlirFloatTF32TypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFloatTF32TypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsATF32(type) + +Checks whether the given type is an TF32 type. +""" +function mlirTypeIsATF32(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsATF32(type::MlirType)::Bool +end + +""" + mlirTF32TypeGet(ctx) + +Creates a TF32 type in the given context. The type is owned by the context. +""" +function mlirTF32TypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirTF32TypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirNoneTypeGetTypeID() + +Returns the typeID of an None type. +""" +function mlirNoneTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirNoneTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsANone(type) + +Checks whether the given type is a None type. +""" +function mlirTypeIsANone(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsANone(type::MlirType)::Bool +end + +""" + mlirNoneTypeGet(ctx) + +Creates a None type in the given context. The type is owned by the context. +""" +function mlirNoneTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirNoneTypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirComplexTypeGetTypeID() + +Returns the typeID of an Complex type. +""" +function mlirComplexTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirComplexTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAComplex(type) + +Checks whether the given type is a Complex type. +""" +function mlirTypeIsAComplex(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAComplex(type::MlirType)::Bool +end + +""" + mlirComplexTypeGet(elementType) + +Creates a complex type with the given element type in the same context as the element type. The type is owned by the context. +""" +function mlirComplexTypeGet(elementType) + @ccall (MLIR_C_PATH[]).mlirComplexTypeGet(elementType::MlirType)::MlirType +end + +""" + mlirComplexTypeGetElementType(type) + +Returns the element type of the given complex type. +""" +function mlirComplexTypeGetElementType(type) + @ccall (MLIR_C_PATH[]).mlirComplexTypeGetElementType(type::MlirType)::MlirType +end + +""" + mlirTypeIsAShaped(type) + +Checks whether the given type is a Shaped type. +""" +function mlirTypeIsAShaped(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAShaped(type::MlirType)::Bool +end + +""" + mlirShapedTypeGetElementType(type) + +Returns the element type of the shaped type. +""" +function mlirShapedTypeGetElementType(type) + @ccall (MLIR_C_PATH[]).mlirShapedTypeGetElementType(type::MlirType)::MlirType +end + +""" + mlirShapedTypeHasRank(type) + +Checks whether the given shaped type is ranked. +""" +function mlirShapedTypeHasRank(type) + @ccall (MLIR_C_PATH[]).mlirShapedTypeHasRank(type::MlirType)::Bool +end + +""" + mlirShapedTypeGetRank(type) + +Returns the rank of the given ranked shaped type. +""" +function mlirShapedTypeGetRank(type) + @ccall (MLIR_C_PATH[]).mlirShapedTypeGetRank(type::MlirType)::Int64 +end + +""" + mlirShapedTypeHasStaticShape(type) + +Checks whether the given shaped type has a static shape. +""" +function mlirShapedTypeHasStaticShape(type) + @ccall (MLIR_C_PATH[]).mlirShapedTypeHasStaticShape(type::MlirType)::Bool +end + +""" + mlirShapedTypeIsDynamicDim(type, dim) + +Checks wither the dim-th dimension of the given shaped type is dynamic. +""" +function mlirShapedTypeIsDynamicDim(type, dim) + @ccall (MLIR_C_PATH[]).mlirShapedTypeIsDynamicDim(type::MlirType, dim::intptr_t)::Bool +end + +""" + mlirShapedTypeGetDimSize(type, dim) + +Returns the dim-th dimension of the given ranked shaped type. +""" +function mlirShapedTypeGetDimSize(type, dim) + @ccall (MLIR_C_PATH[]).mlirShapedTypeGetDimSize(type::MlirType, dim::intptr_t)::Int64 +end + +""" + mlirShapedTypeIsDynamicSize(size) + +Checks whether the given value is used as a placeholder for dynamic sizes in shaped types. +""" +function mlirShapedTypeIsDynamicSize(size) + @ccall (MLIR_C_PATH[]).mlirShapedTypeIsDynamicSize(size::Int64)::Bool +end + +""" + mlirShapedTypeGetDynamicSize() + +Returns the value indicating a dynamic size in a shaped type. Prefer [`mlirShapedTypeIsDynamicSize`](@ref) to direct comparisons with this value. +""" +function mlirShapedTypeGetDynamicSize() + @ccall (MLIR_C_PATH[]).mlirShapedTypeGetDynamicSize()::Int64 +end + +""" + mlirShapedTypeIsDynamicStrideOrOffset(val) + +Checks whether the given value is used as a placeholder for dynamic strides and offsets in shaped types. +""" +function mlirShapedTypeIsDynamicStrideOrOffset(val) + @ccall (MLIR_C_PATH[]).mlirShapedTypeIsDynamicStrideOrOffset(val::Int64)::Bool +end + +""" + mlirShapedTypeGetDynamicStrideOrOffset() + +Returns the value indicating a dynamic stride or offset in a shaped type. Prefer [`mlirShapedTypeGetDynamicStrideOrOffset`](@ref) to direct comparisons with this value. +""" +function mlirShapedTypeGetDynamicStrideOrOffset() + @ccall (MLIR_C_PATH[]).mlirShapedTypeGetDynamicStrideOrOffset()::Int64 +end + +""" + mlirVectorTypeGetTypeID() + +Returns the typeID of an Vector type. +""" +function mlirVectorTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirVectorTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAVector(type) + +Checks whether the given type is a Vector type. +""" +function mlirTypeIsAVector(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAVector(type::MlirType)::Bool +end + +""" + mlirVectorTypeGet(rank, shape, elementType) + +Creates a vector type of the shape identified by its rank and dimensions, with the given element type in the same context as the element type. The type is owned by the context. +""" +function mlirVectorTypeGet(rank, shape, elementType) + @ccall (MLIR_C_PATH[]).mlirVectorTypeGet( + rank::intptr_t, shape::Ptr{Int64}, elementType::MlirType + )::MlirType +end + +""" + mlirVectorTypeGetChecked(loc, rank, shape, elementType) + +Same as "[`mlirVectorTypeGet`](@ref)" but returns a nullptr wrapping [`MlirType`](@ref) on illegal arguments, emitting appropriate diagnostics. +""" +function mlirVectorTypeGetChecked(loc, rank, shape, elementType) + @ccall (MLIR_C_PATH[]).mlirVectorTypeGetChecked( + loc::MlirLocation, rank::intptr_t, shape::Ptr{Int64}, elementType::MlirType + )::MlirType +end + +""" + mlirVectorTypeGetScalable(rank, shape, scalable, elementType) + +Creates a scalable vector type with the shape identified by its rank and dimensions. A subset of dimensions may be marked as scalable via the corresponding flag list, which is expected to have as many entries as the rank of the vector. The vector is created in the same context as the element type. +""" +function mlirVectorTypeGetScalable(rank, shape, scalable, elementType) + @ccall (MLIR_C_PATH[]).mlirVectorTypeGetScalable( + rank::intptr_t, shape::Ptr{Int64}, scalable::Ptr{Bool}, elementType::MlirType + )::MlirType +end + +""" + mlirVectorTypeGetScalableChecked(loc, rank, shape, scalable, elementType) + +Same as "[`mlirVectorTypeGetScalable`](@ref)" but returns a nullptr wrapping [`MlirType`](@ref) on illegal arguments, emitting appropriate diagnostics. +""" +function mlirVectorTypeGetScalableChecked(loc, rank, shape, scalable, elementType) + @ccall (MLIR_C_PATH[]).mlirVectorTypeGetScalableChecked( + loc::MlirLocation, + rank::intptr_t, + shape::Ptr{Int64}, + scalable::Ptr{Bool}, + elementType::MlirType, + )::MlirType +end + +""" + mlirVectorTypeIsScalable(type) + +Checks whether the given vector type is scalable, i.e., has at least one scalable dimension. +""" +function mlirVectorTypeIsScalable(type) + @ccall (MLIR_C_PATH[]).mlirVectorTypeIsScalable(type::MlirType)::Bool +end + +""" + mlirVectorTypeIsDimScalable(type, dim) + +Checks whether the "dim"-th dimension of the given vector is scalable. +""" +function mlirVectorTypeIsDimScalable(type, dim) + @ccall (MLIR_C_PATH[]).mlirVectorTypeIsDimScalable(type::MlirType, dim::intptr_t)::Bool +end + +""" + mlirTypeIsATensor(type) + +Checks whether the given type is a Tensor type. +""" +function mlirTypeIsATensor(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsATensor(type::MlirType)::Bool +end + +""" + mlirRankedTensorTypeGetTypeID() + +Returns the typeID of an RankedTensor type. +""" +function mlirRankedTensorTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirRankedTensorTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsARankedTensor(type) + +Checks whether the given type is a ranked tensor type. +""" +function mlirTypeIsARankedTensor(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsARankedTensor(type::MlirType)::Bool +end + +""" + mlirUnrankedTensorTypeGetTypeID() + +Returns the typeID of an UnrankedTensor type. +""" +function mlirUnrankedTensorTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirUnrankedTensorTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAUnrankedTensor(type) + +Checks whether the given type is an unranked tensor type. +""" +function mlirTypeIsAUnrankedTensor(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAUnrankedTensor(type::MlirType)::Bool +end + +""" + mlirRankedTensorTypeGet(rank, shape, elementType, encoding) + +Creates a tensor type of a fixed rank with the given shape, element type, and optional encoding in the same context as the element type. The type is owned by the context. Tensor types without any specific encoding field should assign [`mlirAttributeGetNull`](@ref)() to this parameter. +""" +function mlirRankedTensorTypeGet(rank, shape, elementType, encoding) + @ccall (MLIR_C_PATH[]).mlirRankedTensorTypeGet( + rank::intptr_t, shape::Ptr{Int64}, elementType::MlirType, encoding::MlirAttribute + )::MlirType +end + +""" + mlirRankedTensorTypeGetChecked(loc, rank, shape, elementType, encoding) + +Same as "[`mlirRankedTensorTypeGet`](@ref)" but returns a nullptr wrapping [`MlirType`](@ref) on illegal arguments, emitting appropriate diagnostics. +""" +function mlirRankedTensorTypeGetChecked(loc, rank, shape, elementType, encoding) + @ccall (MLIR_C_PATH[]).mlirRankedTensorTypeGetChecked( + loc::MlirLocation, + rank::intptr_t, + shape::Ptr{Int64}, + elementType::MlirType, + encoding::MlirAttribute, + )::MlirType +end + +""" + mlirRankedTensorTypeGetEncoding(type) + +Gets the 'encoding' attribute from the ranked tensor type, returning a null attribute if none. +""" +function mlirRankedTensorTypeGetEncoding(type) + @ccall (MLIR_C_PATH[]).mlirRankedTensorTypeGetEncoding(type::MlirType)::MlirAttribute +end + +""" + mlirUnrankedTensorTypeGet(elementType) + +Creates an unranked tensor type with the given element type in the same context as the element type. The type is owned by the context. +""" +function mlirUnrankedTensorTypeGet(elementType) + @ccall (MLIR_C_PATH[]).mlirUnrankedTensorTypeGet(elementType::MlirType)::MlirType +end + +""" + mlirUnrankedTensorTypeGetChecked(loc, elementType) + +Same as "[`mlirUnrankedTensorTypeGet`](@ref)" but returns a nullptr wrapping [`MlirType`](@ref) on illegal arguments, emitting appropriate diagnostics. +""" +function mlirUnrankedTensorTypeGetChecked(loc, elementType) + @ccall (MLIR_C_PATH[]).mlirUnrankedTensorTypeGetChecked( + loc::MlirLocation, elementType::MlirType + )::MlirType +end + +""" + mlirMemRefTypeGetTypeID() + +Returns the typeID of an MemRef type. +""" +function mlirMemRefTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirMemRefTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAMemRef(type) + +Checks whether the given type is a MemRef type. +""" +function mlirTypeIsAMemRef(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAMemRef(type::MlirType)::Bool +end + +""" + mlirUnrankedMemRefTypeGetTypeID() + +Returns the typeID of an UnrankedMemRef type. +""" +function mlirUnrankedMemRefTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirUnrankedMemRefTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAUnrankedMemRef(type) + +Checks whether the given type is an UnrankedMemRef type. +""" +function mlirTypeIsAUnrankedMemRef(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAUnrankedMemRef(type::MlirType)::Bool +end + +""" + mlirMemRefTypeGet(elementType, rank, shape, layout, memorySpace) + +Creates a MemRef type with the given rank and shape, a potentially empty list of affine layout maps, the given memory space and element type, in the same context as element type. The type is owned by the context. +""" +function mlirMemRefTypeGet(elementType, rank, shape, layout, memorySpace) + @ccall (MLIR_C_PATH[]).mlirMemRefTypeGet( + elementType::MlirType, + rank::intptr_t, + shape::Ptr{Int64}, + layout::MlirAttribute, + memorySpace::MlirAttribute, + )::MlirType +end + +""" + mlirMemRefTypeGetChecked(loc, elementType, rank, shape, layout, memorySpace) + +Same as "[`mlirMemRefTypeGet`](@ref)" but returns a nullptr-wrapping [`MlirType`](@ref) o illegal arguments, emitting appropriate diagnostics. +""" +function mlirMemRefTypeGetChecked(loc, elementType, rank, shape, layout, memorySpace) + @ccall (MLIR_C_PATH[]).mlirMemRefTypeGetChecked( + loc::MlirLocation, + elementType::MlirType, + rank::intptr_t, + shape::Ptr{Int64}, + layout::MlirAttribute, + memorySpace::MlirAttribute, + )::MlirType +end + +""" + mlirMemRefTypeContiguousGet(elementType, rank, shape, memorySpace) + +Creates a MemRef type with the given rank, shape, memory space and element type in the same context as the element type. The type has no affine maps, i.e. represents a default row-major contiguous memref. The type is owned by the context. +""" +function mlirMemRefTypeContiguousGet(elementType, rank, shape, memorySpace) + @ccall (MLIR_C_PATH[]).mlirMemRefTypeContiguousGet( + elementType::MlirType, rank::intptr_t, shape::Ptr{Int64}, memorySpace::MlirAttribute + )::MlirType +end + +""" + mlirMemRefTypeContiguousGetChecked(loc, elementType, rank, shape, memorySpace) + +Same as "[`mlirMemRefTypeContiguousGet`](@ref)" but returns a nullptr wrapping [`MlirType`](@ref) on illegal arguments, emitting appropriate diagnostics. +""" +function mlirMemRefTypeContiguousGetChecked(loc, elementType, rank, shape, memorySpace) + @ccall (MLIR_C_PATH[]).mlirMemRefTypeContiguousGetChecked( + loc::MlirLocation, + elementType::MlirType, + rank::intptr_t, + shape::Ptr{Int64}, + memorySpace::MlirAttribute, + )::MlirType +end + +""" + mlirUnrankedMemRefTypeGet(elementType, memorySpace) + +Creates an Unranked MemRef type with the given element type and in the given memory space. The type is owned by the context of element type. +""" +function mlirUnrankedMemRefTypeGet(elementType, memorySpace) + @ccall (MLIR_C_PATH[]).mlirUnrankedMemRefTypeGet( + elementType::MlirType, memorySpace::MlirAttribute + )::MlirType +end + +""" + mlirUnrankedMemRefTypeGetChecked(loc, elementType, memorySpace) + +Same as "[`mlirUnrankedMemRefTypeGet`](@ref)" but returns a nullptr wrapping [`MlirType`](@ref) on illegal arguments, emitting appropriate diagnostics. +""" +function mlirUnrankedMemRefTypeGetChecked(loc, elementType, memorySpace) + @ccall (MLIR_C_PATH[]).mlirUnrankedMemRefTypeGetChecked( + loc::MlirLocation, elementType::MlirType, memorySpace::MlirAttribute + )::MlirType +end + +""" + mlirMemRefTypeGetLayout(type) + +Returns the layout of the given MemRef type. +""" +function mlirMemRefTypeGetLayout(type) + @ccall (MLIR_C_PATH[]).mlirMemRefTypeGetLayout(type::MlirType)::MlirAttribute +end + +""" + mlirMemRefTypeGetAffineMap(type) + +Returns the affine map of the given MemRef type. +""" +function mlirMemRefTypeGetAffineMap(type) + @ccall (MLIR_C_PATH[]).mlirMemRefTypeGetAffineMap(type::MlirType)::MlirAffineMap +end + +""" + mlirMemRefTypeGetMemorySpace(type) + +Returns the memory space of the given MemRef type. +""" +function mlirMemRefTypeGetMemorySpace(type) + @ccall (MLIR_C_PATH[]).mlirMemRefTypeGetMemorySpace(type::MlirType)::MlirAttribute +end + +""" + mlirMemRefTypeGetStridesAndOffset(type, strides, offset) + +Returns the strides of the MemRef if the layout map is in strided form. Both strides and offset are out params. strides must point to pre-allocated memory of length equal to the rank of the memref. +""" +function mlirMemRefTypeGetStridesAndOffset(type, strides, offset) + @ccall (MLIR_C_PATH[]).mlirMemRefTypeGetStridesAndOffset( + type::MlirType, strides::Ptr{Int64}, offset::Ptr{Int64} + )::MlirLogicalResult +end + +""" + mlirUnrankedMemrefGetMemorySpace(type) + +Returns the memory spcae of the given Unranked MemRef type. +""" +function mlirUnrankedMemrefGetMemorySpace(type) + @ccall (MLIR_C_PATH[]).mlirUnrankedMemrefGetMemorySpace(type::MlirType)::MlirAttribute +end + +""" + mlirTupleTypeGetTypeID() + +Returns the typeID of an Tuple type. +""" +function mlirTupleTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirTupleTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsATuple(type) + +Checks whether the given type is a tuple type. +""" +function mlirTypeIsATuple(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsATuple(type::MlirType)::Bool +end + +""" + mlirTupleTypeGet(ctx, numElements, elements) + +Creates a tuple type that consists of the given list of elemental types. The type is owned by the context. +""" +function mlirTupleTypeGet(ctx, numElements, elements) + @ccall (MLIR_C_PATH[]).mlirTupleTypeGet( + ctx::MlirContext, numElements::intptr_t, elements::Ptr{MlirType} + )::MlirType +end + +""" + mlirTupleTypeGetNumTypes(type) + +Returns the number of types contained in a tuple. +""" +function mlirTupleTypeGetNumTypes(type) + @ccall (MLIR_C_PATH[]).mlirTupleTypeGetNumTypes(type::MlirType)::intptr_t +end + +""" + mlirTupleTypeGetType(type, pos) + +Returns the pos-th type in the tuple type. +""" +function mlirTupleTypeGetType(type, pos) + @ccall (MLIR_C_PATH[]).mlirTupleTypeGetType(type::MlirType, pos::intptr_t)::MlirType +end + +""" + mlirFunctionTypeGetTypeID() + +Returns the typeID of an Function type. +""" +function mlirFunctionTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirFunctionTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAFunction(type) + +Checks whether the given type is a function type. +""" +function mlirTypeIsAFunction(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAFunction(type::MlirType)::Bool +end + +""" + mlirFunctionTypeGet(ctx, numInputs, inputs, numResults, results) + +Creates a function type, mapping a list of input types to result types. +""" +function mlirFunctionTypeGet(ctx, numInputs, inputs, numResults, results) + @ccall (MLIR_C_PATH[]).mlirFunctionTypeGet( + ctx::MlirContext, + numInputs::intptr_t, + inputs::Ptr{MlirType}, + numResults::intptr_t, + results::Ptr{MlirType}, + )::MlirType +end + +""" + mlirFunctionTypeGetNumInputs(type) + +Returns the number of input types. +""" +function mlirFunctionTypeGetNumInputs(type) + @ccall (MLIR_C_PATH[]).mlirFunctionTypeGetNumInputs(type::MlirType)::intptr_t +end + +""" + mlirFunctionTypeGetNumResults(type) + +Returns the number of result types. +""" +function mlirFunctionTypeGetNumResults(type) + @ccall (MLIR_C_PATH[]).mlirFunctionTypeGetNumResults(type::MlirType)::intptr_t +end + +""" + mlirFunctionTypeGetInput(type, pos) + +Returns the pos-th input type. +""" +function mlirFunctionTypeGetInput(type, pos) + @ccall (MLIR_C_PATH[]).mlirFunctionTypeGetInput(type::MlirType, pos::intptr_t)::MlirType +end + +""" + mlirFunctionTypeGetResult(type, pos) + +Returns the pos-th result type. +""" +function mlirFunctionTypeGetResult(type, pos) + @ccall (MLIR_C_PATH[]).mlirFunctionTypeGetResult( + type::MlirType, pos::intptr_t + )::MlirType +end + +""" + mlirOpaqueTypeGetTypeID() + +Returns the typeID of an Opaque type. +""" +function mlirOpaqueTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirOpaqueTypeGetTypeID()::MlirTypeID +end + +""" + mlirTypeIsAOpaque(type) + +Checks whether the given type is an opaque type. +""" +function mlirTypeIsAOpaque(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAOpaque(type::MlirType)::Bool +end + +""" + mlirOpaqueTypeGet(ctx, dialectNamespace, typeData) + +Creates an opaque type in the given context associated with the dialect identified by its namespace. The type contains opaque byte data of the specified length (data need not be null-terminated). +""" +function mlirOpaqueTypeGet(ctx, dialectNamespace, typeData) + @ccall (MLIR_C_PATH[]).mlirOpaqueTypeGet( + ctx::MlirContext, dialectNamespace::MlirStringRef, typeData::MlirStringRef + )::MlirType +end + +""" + mlirOpaqueTypeGetDialectNamespace(type) + +Returns the namespace of the dialect with which the given opaque type is associated. The namespace string is owned by the context. +""" +function mlirOpaqueTypeGetDialectNamespace(type) + @ccall (MLIR_C_PATH[]).mlirOpaqueTypeGetDialectNamespace(type::MlirType)::MlirStringRef +end + +""" + mlirOpaqueTypeGetData(type) + +Returns the raw data as a string reference. The data remains live as long as the context in which the type lives. +""" +function mlirOpaqueTypeGetData(type) + @ccall (MLIR_C_PATH[]).mlirOpaqueTypeGetData(type::MlirType)::MlirStringRef +end + +""" + mlirPassManagerCreate(ctx) + +Create a new top-level PassManager with the default anchor. +""" +function mlirPassManagerCreate(ctx) + @ccall (MLIR_C_PATH[]).mlirPassManagerCreate(ctx::MlirContext)::MlirPassManager +end + +""" + mlirPassManagerCreateOnOperation(ctx, anchorOp) + +Create a new top-level PassManager anchored on `anchorOp`. +""" +function mlirPassManagerCreateOnOperation(ctx, anchorOp) + @ccall (MLIR_C_PATH[]).mlirPassManagerCreateOnOperation( + ctx::MlirContext, anchorOp::MlirStringRef + )::MlirPassManager +end + +""" + mlirPassManagerDestroy(passManager) + +Destroy the provided PassManager. +""" +function mlirPassManagerDestroy(passManager) + @ccall (MLIR_C_PATH[]).mlirPassManagerDestroy(passManager::MlirPassManager)::Cvoid +end + +""" + mlirPassManagerIsNull(passManager) + +Checks if a PassManager is null. +""" +function mlirPassManagerIsNull(passManager) + @ccall (MLIR_C_PATH[]).mlirPassManagerIsNull(passManager::MlirPassManager)::Bool +end + +""" + mlirPassManagerGetAsOpPassManager(passManager) + +Cast a top-level PassManager to a generic OpPassManager. +""" +function mlirPassManagerGetAsOpPassManager(passManager) + @ccall (MLIR_C_PATH[]).mlirPassManagerGetAsOpPassManager( + passManager::MlirPassManager + )::MlirOpPassManager +end + +""" + mlirPassManagerRunOnOp(passManager, op) + +Run the provided `passManager` on the given `op`. +""" +function mlirPassManagerRunOnOp(passManager, op) + @ccall (MLIR_C_PATH[]).mlirPassManagerRunOnOp( + passManager::MlirPassManager, op::MlirOperation + )::MlirLogicalResult +end + +""" + mlirPassManagerEnableIRPrinting(passManager) + +Enable mlir-print-ir-after-all. +""" +function mlirPassManagerEnableIRPrinting(passManager) + @ccall (MLIR_C_PATH[]).mlirPassManagerEnableIRPrinting( + passManager::MlirPassManager + )::Cvoid +end + +""" + mlirPassManagerEnableVerifier(passManager, enable) + +Enable / disable verify-each. +""" +function mlirPassManagerEnableVerifier(passManager, enable) + @ccall (MLIR_C_PATH[]).mlirPassManagerEnableVerifier( + passManager::MlirPassManager, enable::Bool + )::Cvoid +end + +""" + mlirPassManagerGetNestedUnder(passManager, operationName) + +Nest an OpPassManager under the top-level PassManager, the nested passmanager will only run on operations matching the provided name. The returned OpPassManager will be destroyed when the parent is destroyed. To further nest more OpPassManager under the newly returned one, see `mlirOpPassManagerNest` below. +""" +function mlirPassManagerGetNestedUnder(passManager, operationName) + @ccall (MLIR_C_PATH[]).mlirPassManagerGetNestedUnder( + passManager::MlirPassManager, operationName::MlirStringRef + )::MlirOpPassManager +end + +""" + mlirOpPassManagerGetNestedUnder(passManager, operationName) + +Nest an OpPassManager under the provided OpPassManager, the nested passmanager will only run on operations matching the provided name. The returned OpPassManager will be destroyed when the parent is destroyed. +""" +function mlirOpPassManagerGetNestedUnder(passManager, operationName) + @ccall (MLIR_C_PATH[]).mlirOpPassManagerGetNestedUnder( + passManager::MlirOpPassManager, operationName::MlirStringRef + )::MlirOpPassManager +end + +""" + mlirPassManagerAddOwnedPass(passManager, pass) + +Add a pass and transfer ownership to the provided top-level mlirPassManager. If the pass is not a generic operation pass or a ModulePass, a new OpPassManager is implicitly nested under the provided PassManager. +""" +function mlirPassManagerAddOwnedPass(passManager, pass) + @ccall (MLIR_C_PATH[]).mlirPassManagerAddOwnedPass( + passManager::MlirPassManager, pass::MlirPass + )::Cvoid +end + +""" + mlirOpPassManagerAddOwnedPass(passManager, pass) + +Add a pass and transfer ownership to the provided mlirOpPassManager. If the pass is not a generic operation pass or matching the type of the provided PassManager, a new OpPassManager is implicitly nested under the provided PassManager. +""" +function mlirOpPassManagerAddOwnedPass(passManager, pass) + @ccall (MLIR_C_PATH[]).mlirOpPassManagerAddOwnedPass( + passManager::MlirOpPassManager, pass::MlirPass + )::Cvoid +end + +""" + mlirOpPassManagerAddPipeline(passManager, pipelineElements, callback, userData) + +Parse a sequence of textual MLIR pass pipeline elements and add them to the provided OpPassManager. If parsing fails an error message is reported using the provided callback. +""" +function mlirOpPassManagerAddPipeline(passManager, pipelineElements, callback, userData) + @ccall (MLIR_C_PATH[]).mlirOpPassManagerAddPipeline( + passManager::MlirOpPassManager, + pipelineElements::MlirStringRef, + callback::MlirStringCallback, + userData::Ptr{Cvoid}, + )::MlirLogicalResult +end + +""" + mlirPrintPassPipeline(passManager, callback, userData) + +Print a textual MLIR pass pipeline by sending chunks of the string representation and forwarding `userData to `callback`. Note that the callback may be called several times with consecutive chunks of the string. +""" +function mlirPrintPassPipeline(passManager, callback, userData) + @ccall (MLIR_C_PATH[]).mlirPrintPassPipeline( + passManager::MlirOpPassManager, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirParsePassPipeline(passManager, pipeline, callback, userData) + +Parse a textual MLIR pass pipeline and assign it to the provided OpPassManager. If parsing fails an error message is reported using the provided callback. +""" +function mlirParsePassPipeline(passManager, pipeline, callback, userData) + @ccall (MLIR_C_PATH[]).mlirParsePassPipeline( + passManager::MlirOpPassManager, + pipeline::MlirStringRef, + callback::MlirStringCallback, + userData::Ptr{Cvoid}, + )::MlirLogicalResult +end + +""" + mlirCreateExternalPass(passID, name, argument, description, opName, nDependentDialects, dependentDialects, callbacks, userData) + +Creates an external [`MlirPass`](@ref) that calls the supplied `callbacks` using the supplied `userData`. If `opName` is empty, the pass is a generic operation pass. Otherwise it is an operation pass specific to the specified pass name. +""" +function mlirCreateExternalPass( + passID, + name, + argument, + description, + opName, + nDependentDialects, + dependentDialects, + callbacks, + userData, +) + @ccall (MLIR_C_PATH[]).mlirCreateExternalPass( + passID::MlirTypeID, + name::MlirStringRef, + argument::MlirStringRef, + description::MlirStringRef, + opName::MlirStringRef, + nDependentDialects::intptr_t, + dependentDialects::Ptr{MlirDialectHandle}, + callbacks::MlirExternalPassCallbacks, + userData::Ptr{Cvoid}, + )::MlirPass +end + +""" + mlirExternalPassSignalFailure(pass) + +This signals that the pass has failed. This is only valid to call during the `run` callback of [`MlirExternalPassCallbacks`](@ref). See Pass::signalPassFailure(). +""" +function mlirExternalPassSignalFailure(pass) + @ccall (MLIR_C_PATH[]).mlirExternalPassSignalFailure(pass::MlirExternalPass)::Cvoid +end + +function mlirRegisterConversionPasses() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionPasses()::Cvoid +end + +function mlirCreateConversionArithToAMDGPUConversionPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionArithToAMDGPUConversionPass()::MlirPass +end + +function mlirRegisterConversionArithToAMDGPUConversionPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionArithToAMDGPUConversionPass()::Cvoid +end + +function mlirCreateConversionArithToArmSMEConversionPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionArithToArmSMEConversionPass()::MlirPass +end + +function mlirRegisterConversionArithToArmSMEConversionPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionArithToArmSMEConversionPass()::Cvoid +end + +function mlirCreateConversionArithToLLVMConversionPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionArithToLLVMConversionPass()::MlirPass +end + +function mlirRegisterConversionArithToLLVMConversionPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionArithToLLVMConversionPass()::Cvoid +end + +function mlirCreateConversionConvertAMDGPUToROCDL() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertAMDGPUToROCDL()::MlirPass +end + +function mlirRegisterConversionConvertAMDGPUToROCDL() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertAMDGPUToROCDL()::Cvoid +end + +function mlirCreateConversionConvertAffineForToGPU() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertAffineForToGPU()::MlirPass +end + +function mlirRegisterConversionConvertAffineForToGPU() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertAffineForToGPU()::Cvoid +end + +function mlirCreateConversionConvertAffineToStandard() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertAffineToStandard()::MlirPass +end + +function mlirRegisterConversionConvertAffineToStandard() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertAffineToStandard()::Cvoid +end + +function mlirCreateConversionConvertArithToEmitC() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertArithToEmitC()::MlirPass +end + +function mlirRegisterConversionConvertArithToEmitC() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertArithToEmitC()::Cvoid +end + +function mlirCreateConversionConvertArithToSPIRV() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertArithToSPIRV()::MlirPass +end + +function mlirRegisterConversionConvertArithToSPIRV() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertArithToSPIRV()::Cvoid +end + +function mlirCreateConversionConvertArmNeon2dToIntr() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertArmNeon2dToIntr()::MlirPass +end + +function mlirRegisterConversionConvertArmNeon2dToIntr() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertArmNeon2dToIntr()::Cvoid +end + +function mlirCreateConversionConvertArmSMEToLLVM() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertArmSMEToLLVM()::MlirPass +end + +function mlirRegisterConversionConvertArmSMEToLLVM() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertArmSMEToLLVM()::Cvoid +end + +function mlirCreateConversionConvertArmSMEToSCF() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertArmSMEToSCF()::MlirPass +end + +function mlirRegisterConversionConvertArmSMEToSCF() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertArmSMEToSCF()::Cvoid +end + +function mlirCreateConversionConvertAsyncToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertAsyncToLLVMPass()::MlirPass +end + +function mlirRegisterConversionConvertAsyncToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertAsyncToLLVMPass()::Cvoid +end + +function mlirCreateConversionConvertBufferizationToMemRef() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertBufferizationToMemRef()::MlirPass +end + +function mlirRegisterConversionConvertBufferizationToMemRef() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertBufferizationToMemRef()::Cvoid +end + +function mlirCreateConversionConvertComplexToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertComplexToLLVMPass()::MlirPass +end + +function mlirRegisterConversionConvertComplexToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertComplexToLLVMPass()::Cvoid +end + +function mlirCreateConversionConvertComplexToLibm() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertComplexToLibm()::MlirPass +end + +function mlirRegisterConversionConvertComplexToLibm() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertComplexToLibm()::Cvoid +end + +function mlirCreateConversionConvertComplexToSPIRVPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertComplexToSPIRVPass()::MlirPass +end + +function mlirRegisterConversionConvertComplexToSPIRVPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertComplexToSPIRVPass()::Cvoid +end + +function mlirCreateConversionConvertComplexToStandard() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertComplexToStandard()::MlirPass +end + +function mlirRegisterConversionConvertComplexToStandard() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertComplexToStandard()::Cvoid +end + +function mlirCreateConversionConvertControlFlowToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertControlFlowToLLVMPass()::MlirPass +end + +function mlirRegisterConversionConvertControlFlowToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertControlFlowToLLVMPass()::Cvoid +end + +function mlirCreateConversionConvertControlFlowToSPIRV() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertControlFlowToSPIRV()::MlirPass +end + +function mlirRegisterConversionConvertControlFlowToSPIRV() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertControlFlowToSPIRV()::Cvoid +end + +function mlirCreateConversionConvertFuncToEmitC() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertFuncToEmitC()::MlirPass +end + +function mlirRegisterConversionConvertFuncToEmitC() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertFuncToEmitC()::Cvoid +end + +function mlirCreateConversionConvertFuncToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertFuncToLLVMPass()::MlirPass +end + +function mlirRegisterConversionConvertFuncToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertFuncToLLVMPass()::Cvoid +end + +function mlirCreateConversionConvertFuncToSPIRV() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertFuncToSPIRV()::MlirPass +end + +function mlirRegisterConversionConvertFuncToSPIRV() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertFuncToSPIRV()::Cvoid +end + +function mlirCreateConversionConvertGPUToSPIRV() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertGPUToSPIRV()::MlirPass +end + +function mlirRegisterConversionConvertGPUToSPIRV() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertGPUToSPIRV()::Cvoid +end + +function mlirCreateConversionConvertGpuLaunchFuncToVulkanLaunchFunc() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertGpuLaunchFuncToVulkanLaunchFunc()::MlirPass +end + +function mlirRegisterConversionConvertGpuLaunchFuncToVulkanLaunchFunc() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertGpuLaunchFuncToVulkanLaunchFunc()::Cvoid +end + +function mlirCreateConversionConvertGpuOpsToLLVMSPVOps() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertGpuOpsToLLVMSPVOps()::MlirPass +end + +function mlirRegisterConversionConvertGpuOpsToLLVMSPVOps() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertGpuOpsToLLVMSPVOps()::Cvoid +end + +function mlirCreateConversionConvertGpuOpsToNVVMOps() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertGpuOpsToNVVMOps()::MlirPass +end + +function mlirRegisterConversionConvertGpuOpsToNVVMOps() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertGpuOpsToNVVMOps()::Cvoid +end + +function mlirCreateConversionConvertGpuOpsToROCDLOps() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertGpuOpsToROCDLOps()::MlirPass +end + +function mlirRegisterConversionConvertGpuOpsToROCDLOps() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertGpuOpsToROCDLOps()::Cvoid +end + +function mlirCreateConversionConvertIndexToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertIndexToLLVMPass()::MlirPass +end + +function mlirRegisterConversionConvertIndexToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertIndexToLLVMPass()::Cvoid +end + +function mlirCreateConversionConvertIndexToSPIRVPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertIndexToSPIRVPass()::MlirPass +end + +function mlirRegisterConversionConvertIndexToSPIRVPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertIndexToSPIRVPass()::Cvoid +end + +function mlirCreateConversionConvertLinalgToStandard() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertLinalgToStandard()::MlirPass +end + +function mlirRegisterConversionConvertLinalgToStandard() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertLinalgToStandard()::Cvoid +end + +function mlirCreateConversionConvertMathToFuncs() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertMathToFuncs()::MlirPass +end + +function mlirRegisterConversionConvertMathToFuncs() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertMathToFuncs()::Cvoid +end + +function mlirCreateConversionConvertMathToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertMathToLLVMPass()::MlirPass +end + +function mlirRegisterConversionConvertMathToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertMathToLLVMPass()::Cvoid +end + +function mlirCreateConversionConvertMathToLibm() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertMathToLibm()::MlirPass +end + +function mlirRegisterConversionConvertMathToLibm() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertMathToLibm()::Cvoid +end + +function mlirCreateConversionConvertMathToROCDL() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertMathToROCDL()::MlirPass +end + +function mlirRegisterConversionConvertMathToROCDL() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertMathToROCDL()::Cvoid +end + +function mlirCreateConversionConvertMathToSPIRV() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertMathToSPIRV()::MlirPass +end + +function mlirRegisterConversionConvertMathToSPIRV() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertMathToSPIRV()::Cvoid +end + +function mlirCreateConversionConvertMemRefToEmitC() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertMemRefToEmitC()::MlirPass +end + +function mlirRegisterConversionConvertMemRefToEmitC() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertMemRefToEmitC()::Cvoid +end + +function mlirCreateConversionConvertMemRefToSPIRV() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertMemRefToSPIRV()::MlirPass +end + +function mlirRegisterConversionConvertMemRefToSPIRV() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertMemRefToSPIRV()::Cvoid +end + +function mlirCreateConversionConvertNVGPUToNVVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertNVGPUToNVVMPass()::MlirPass +end + +function mlirRegisterConversionConvertNVGPUToNVVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertNVGPUToNVVMPass()::Cvoid +end + +function mlirCreateConversionConvertNVVMToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertNVVMToLLVMPass()::MlirPass +end + +function mlirRegisterConversionConvertNVVMToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertNVVMToLLVMPass()::Cvoid +end + +function mlirCreateConversionConvertOpenACCToSCF() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertOpenACCToSCF()::MlirPass +end + +function mlirRegisterConversionConvertOpenACCToSCF() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertOpenACCToSCF()::Cvoid +end + +function mlirCreateConversionConvertOpenMPToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertOpenMPToLLVMPass()::MlirPass +end + +function mlirRegisterConversionConvertOpenMPToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertOpenMPToLLVMPass()::Cvoid +end + +function mlirCreateConversionConvertPDLToPDLInterp() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertPDLToPDLInterp()::MlirPass +end + +function mlirRegisterConversionConvertPDLToPDLInterp() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertPDLToPDLInterp()::Cvoid +end + +function mlirCreateConversionConvertParallelLoopToGpu() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertParallelLoopToGpu()::MlirPass +end + +function mlirRegisterConversionConvertParallelLoopToGpu() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertParallelLoopToGpu()::Cvoid +end + +function mlirCreateConversionConvertSCFToOpenMPPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertSCFToOpenMPPass()::MlirPass +end + +function mlirRegisterConversionConvertSCFToOpenMPPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertSCFToOpenMPPass()::Cvoid +end + +function mlirCreateConversionConvertSPIRVToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertSPIRVToLLVMPass()::MlirPass +end + +function mlirRegisterConversionConvertSPIRVToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertSPIRVToLLVMPass()::Cvoid +end + +function mlirCreateConversionConvertShapeConstraints() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertShapeConstraints()::MlirPass +end + +function mlirRegisterConversionConvertShapeConstraints() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertShapeConstraints()::Cvoid +end + +function mlirCreateConversionConvertShapeToStandard() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertShapeToStandard()::MlirPass +end + +function mlirRegisterConversionConvertShapeToStandard() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertShapeToStandard()::Cvoid +end + +function mlirCreateConversionConvertTensorToLinalg() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertTensorToLinalg()::MlirPass +end + +function mlirRegisterConversionConvertTensorToLinalg() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertTensorToLinalg()::Cvoid +end + +function mlirCreateConversionConvertTensorToSPIRV() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertTensorToSPIRV()::MlirPass +end + +function mlirRegisterConversionConvertTensorToSPIRV() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertTensorToSPIRV()::Cvoid +end + +function mlirCreateConversionConvertToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertToLLVMPass()::MlirPass +end + +function mlirRegisterConversionConvertToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertToLLVMPass()::Cvoid +end + +function mlirCreateConversionConvertToSPIRVPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertToSPIRVPass()::MlirPass +end + +function mlirRegisterConversionConvertToSPIRVPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertToSPIRVPass()::Cvoid +end + +function mlirCreateConversionConvertVectorToArmSME() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertVectorToArmSME()::MlirPass +end + +function mlirRegisterConversionConvertVectorToArmSME() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertVectorToArmSME()::Cvoid +end + +function mlirCreateConversionConvertVectorToGPU() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertVectorToGPU()::MlirPass +end + +function mlirRegisterConversionConvertVectorToGPU() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertVectorToGPU()::Cvoid +end + +function mlirCreateConversionConvertVectorToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertVectorToLLVMPass()::MlirPass +end + +function mlirRegisterConversionConvertVectorToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertVectorToLLVMPass()::Cvoid +end + +function mlirCreateConversionConvertVectorToSCF() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertVectorToSCF()::MlirPass +end + +function mlirRegisterConversionConvertVectorToSCF() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertVectorToSCF()::Cvoid +end + +function mlirCreateConversionConvertVectorToSPIRV() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertVectorToSPIRV()::MlirPass +end + +function mlirRegisterConversionConvertVectorToSPIRV() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertVectorToSPIRV()::Cvoid +end + +function mlirCreateConversionConvertVulkanLaunchFuncToVulkanCallsPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionConvertVulkanLaunchFuncToVulkanCallsPass()::MlirPass +end + +function mlirRegisterConversionConvertVulkanLaunchFuncToVulkanCallsPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionConvertVulkanLaunchFuncToVulkanCallsPass()::Cvoid +end + +function mlirCreateConversionFinalizeMemRefToLLVMConversionPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionFinalizeMemRefToLLVMConversionPass()::MlirPass +end + +function mlirRegisterConversionFinalizeMemRefToLLVMConversionPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionFinalizeMemRefToLLVMConversionPass()::Cvoid +end + +function mlirCreateConversionGpuToLLVMConversionPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionGpuToLLVMConversionPass()::MlirPass +end + +function mlirRegisterConversionGpuToLLVMConversionPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionGpuToLLVMConversionPass()::Cvoid +end + +function mlirCreateConversionLiftControlFlowToSCFPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionLiftControlFlowToSCFPass()::MlirPass +end + +function mlirRegisterConversionLiftControlFlowToSCFPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionLiftControlFlowToSCFPass()::Cvoid +end + +function mlirCreateConversionLowerHostCodeToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionLowerHostCodeToLLVMPass()::MlirPass +end + +function mlirRegisterConversionLowerHostCodeToLLVMPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionLowerHostCodeToLLVMPass()::Cvoid +end + +function mlirCreateConversionMapMemRefStorageClass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionMapMemRefStorageClass()::MlirPass +end + +function mlirRegisterConversionMapMemRefStorageClass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionMapMemRefStorageClass()::Cvoid +end + +function mlirCreateConversionReconcileUnrealizedCasts() + @ccall (MLIR_C_PATH[]).mlirCreateConversionReconcileUnrealizedCasts()::MlirPass +end + +function mlirRegisterConversionReconcileUnrealizedCasts() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionReconcileUnrealizedCasts()::Cvoid +end + +function mlirCreateConversionSCFToControlFlow() + @ccall (MLIR_C_PATH[]).mlirCreateConversionSCFToControlFlow()::MlirPass +end + +function mlirRegisterConversionSCFToControlFlow() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionSCFToControlFlow()::Cvoid +end + +function mlirCreateConversionSCFToEmitC() + @ccall (MLIR_C_PATH[]).mlirCreateConversionSCFToEmitC()::MlirPass +end + +function mlirRegisterConversionSCFToEmitC() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionSCFToEmitC()::Cvoid +end + +function mlirCreateConversionSCFToSPIRV() + @ccall (MLIR_C_PATH[]).mlirCreateConversionSCFToSPIRV()::MlirPass +end + +function mlirRegisterConversionSCFToSPIRV() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionSCFToSPIRV()::Cvoid +end + +function mlirCreateConversionSetLLVMModuleDataLayoutPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionSetLLVMModuleDataLayoutPass()::MlirPass +end + +function mlirRegisterConversionSetLLVMModuleDataLayoutPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionSetLLVMModuleDataLayoutPass()::Cvoid +end + +function mlirCreateConversionTosaToArith() + @ccall (MLIR_C_PATH[]).mlirCreateConversionTosaToArith()::MlirPass +end + +function mlirRegisterConversionTosaToArith() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionTosaToArith()::Cvoid +end + +function mlirCreateConversionTosaToLinalg() + @ccall (MLIR_C_PATH[]).mlirCreateConversionTosaToLinalg()::MlirPass +end + +function mlirRegisterConversionTosaToLinalg() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionTosaToLinalg()::Cvoid +end + +function mlirCreateConversionTosaToLinalgNamed() + @ccall (MLIR_C_PATH[]).mlirCreateConversionTosaToLinalgNamed()::MlirPass +end + +function mlirRegisterConversionTosaToLinalgNamed() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionTosaToLinalgNamed()::Cvoid +end + +function mlirCreateConversionTosaToMLProgram() + @ccall (MLIR_C_PATH[]).mlirCreateConversionTosaToMLProgram()::MlirPass +end + +function mlirRegisterConversionTosaToMLProgram() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionTosaToMLProgram()::Cvoid +end + +function mlirCreateConversionTosaToSCF() + @ccall (MLIR_C_PATH[]).mlirCreateConversionTosaToSCF()::MlirPass +end + +function mlirRegisterConversionTosaToSCF() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionTosaToSCF()::Cvoid +end + +function mlirCreateConversionTosaToTensor() + @ccall (MLIR_C_PATH[]).mlirCreateConversionTosaToTensor()::MlirPass +end + +function mlirRegisterConversionTosaToTensor() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionTosaToTensor()::Cvoid +end + +function mlirCreateConversionUBToLLVMConversionPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionUBToLLVMConversionPass()::MlirPass +end + +function mlirRegisterConversionUBToLLVMConversionPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionUBToLLVMConversionPass()::Cvoid +end + +function mlirCreateConversionUBToSPIRVConversionPass() + @ccall (MLIR_C_PATH[]).mlirCreateConversionUBToSPIRVConversionPass()::MlirPass +end + +function mlirRegisterConversionUBToSPIRVConversionPass() + @ccall (MLIR_C_PATH[]).mlirRegisterConversionUBToSPIRVConversionPass()::Cvoid +end + +""" + mlirEnableGlobalDebug(enable) + +Sets the global debugging flag. +""" +function mlirEnableGlobalDebug(enable) + @ccall (MLIR_C_PATH[]).mlirEnableGlobalDebug(enable::Bool)::Cvoid +end + +""" + mlirIsGlobalDebugEnabled() + +Retuns `true` if the global debugging flag is set, false otherwise. +""" +function mlirIsGlobalDebugEnabled() + @ccall (MLIR_C_PATH[]).mlirIsGlobalDebugEnabled()::Bool +end + +""" + mlirSetGlobalDebugType(type) + +Sets the current debug type, similarly to `-debug-only=type` in the command-line tools. Note that global debug should be enabled for any output to be produced. +""" +function mlirSetGlobalDebugType(type) + @ccall (MLIR_C_PATH[]).mlirSetGlobalDebugType(type::Cstring)::Cvoid +end + +""" + mlirSetGlobalDebugTypes(types, n) + +Sets multiple current debug types, similarly to `-debug-only=type1,type2" in the command-line tools. Note that global debug should be enabled for any output to be produced. +""" +function mlirSetGlobalDebugTypes(types, n) + @ccall (MLIR_C_PATH[]).mlirSetGlobalDebugTypes(types::Ptr{Cstring}, n::intptr_t)::Cvoid +end + +""" + mlirIsCurrentDebugType(type) + +Checks if `type` is set as the current debug type. +""" +function mlirIsCurrentDebugType(type) + @ccall (MLIR_C_PATH[]).mlirIsCurrentDebugType(type::Cstring)::Bool +end + +""" + MlirDiagnosticSeverity + +Severity of a diagnostic. +""" +@cenum MlirDiagnosticSeverity::UInt32 begin + MlirDiagnosticError = 0x0000000000000000 + MlirDiagnosticWarning = 0x0000000000000001 + MlirDiagnosticNote = 0x0000000000000002 + MlirDiagnosticRemark = 0x0000000000000003 +end + +""" +Opaque identifier of a diagnostic handler, useful to detach a handler. +""" +const MlirDiagnosticHandlerID = UInt64 + +# typedef MlirLogicalResult ( * MlirDiagnosticHandler ) ( MlirDiagnostic , void * userData ) +""" +Diagnostic handler type. Accepts a reference to a diagnostic, which is only guaranteed to be live during the call. The handler is passed the `userData` that was provided when the handler was attached to a context. If the handler processed the diagnostic completely, it is expected to return success. Otherwise, it is expected to return failure to indicate that other handlers should attempt to process the diagnostic. +""" +const MlirDiagnosticHandler = Ptr{Cvoid} + +""" + mlirDiagnosticPrint(diagnostic, callback, userData) + +Prints a diagnostic using the provided callback. +""" +function mlirDiagnosticPrint(diagnostic, callback, userData) + @ccall (MLIR_C_PATH[]).mlirDiagnosticPrint( + diagnostic::MlirDiagnostic, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirDiagnosticGetLocation(diagnostic) + +Returns the location at which the diagnostic is reported. +""" +function mlirDiagnosticGetLocation(diagnostic) + @ccall (MLIR_C_PATH[]).mlirDiagnosticGetLocation( + diagnostic::MlirDiagnostic + )::MlirLocation +end + +""" + mlirDiagnosticGetSeverity(diagnostic) + +Returns the severity of the diagnostic. +""" +function mlirDiagnosticGetSeverity(diagnostic) + @ccall (MLIR_C_PATH[]).mlirDiagnosticGetSeverity( + diagnostic::MlirDiagnostic + )::MlirDiagnosticSeverity +end + +""" + mlirDiagnosticGetNumNotes(diagnostic) + +Returns the number of notes attached to the diagnostic. +""" +function mlirDiagnosticGetNumNotes(diagnostic) + @ccall (MLIR_C_PATH[]).mlirDiagnosticGetNumNotes(diagnostic::MlirDiagnostic)::intptr_t +end + +""" + mlirDiagnosticGetNote(diagnostic, pos) + +Returns `pos`-th note attached to the diagnostic. Expects `pos` to be a valid zero-based index into the list of notes. +""" +function mlirDiagnosticGetNote(diagnostic, pos) + @ccall (MLIR_C_PATH[]).mlirDiagnosticGetNote( + diagnostic::MlirDiagnostic, pos::intptr_t + )::MlirDiagnostic +end + +""" + mlirContextAttachDiagnosticHandler(context, handler, userData, deleteUserData) + +Attaches the diagnostic handler to the context. Handlers are invoked in the reverse order of attachment until one of them processes the diagnostic completely. When a handler is invoked it is passed the `userData` that was provided when it was attached. If non-NULL, `deleteUserData` is called once the system no longer needs to call the handler (for instance after the handler is detached or the context is destroyed). Returns an identifier that can be used to detach the handler. +""" +function mlirContextAttachDiagnosticHandler(context, handler, userData, deleteUserData) + @ccall (MLIR_C_PATH[]).mlirContextAttachDiagnosticHandler( + context::MlirContext, + handler::MlirDiagnosticHandler, + userData::Ptr{Cvoid}, + deleteUserData::Ptr{Cvoid}, + )::MlirDiagnosticHandlerID +end + +""" + mlirContextDetachDiagnosticHandler(context, id) + +Detaches an attached diagnostic handler from the context given its identifier. +""" +function mlirContextDetachDiagnosticHandler(context, id) + @ccall (MLIR_C_PATH[]).mlirContextDetachDiagnosticHandler( + context::MlirContext, id::MlirDiagnosticHandlerID + )::Cvoid +end + +""" + mlirEmitError(location, message) + +Emits an error at the given location through the diagnostics engine. Used for testing purposes. +""" +function mlirEmitError(location, message) + @ccall (MLIR_C_PATH[]).mlirEmitError(location::MlirLocation, message::Cstring)::Cvoid +end + +function mlirGetDialectHandle__amdgpu__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__amdgpu__()::MlirDialectHandle +end + +function mlirGetDialectHandle__arith__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__arith__()::MlirDialectHandle +end + +function mlirGetDialectHandle__async__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__async__()::MlirDialectHandle +end + +function mlirRegisterAsyncPasses() + @ccall (MLIR_C_PATH[]).mlirRegisterAsyncPasses()::Cvoid +end + +function mlirCreateAsyncAsyncFuncToAsyncRuntime() + @ccall (MLIR_C_PATH[]).mlirCreateAsyncAsyncFuncToAsyncRuntime()::MlirPass +end + +function mlirRegisterAsyncAsyncFuncToAsyncRuntime() + @ccall (MLIR_C_PATH[]).mlirRegisterAsyncAsyncFuncToAsyncRuntime()::Cvoid +end + +function mlirCreateAsyncAsyncParallelFor() + @ccall (MLIR_C_PATH[]).mlirCreateAsyncAsyncParallelFor()::MlirPass +end + +function mlirRegisterAsyncAsyncParallelFor() + @ccall (MLIR_C_PATH[]).mlirRegisterAsyncAsyncParallelFor()::Cvoid +end + +function mlirCreateAsyncAsyncRuntimePolicyBasedRefCounting() + @ccall (MLIR_C_PATH[]).mlirCreateAsyncAsyncRuntimePolicyBasedRefCounting()::MlirPass +end + +function mlirRegisterAsyncAsyncRuntimePolicyBasedRefCounting() + @ccall (MLIR_C_PATH[]).mlirRegisterAsyncAsyncRuntimePolicyBasedRefCounting()::Cvoid +end + +function mlirCreateAsyncAsyncRuntimeRefCounting() + @ccall (MLIR_C_PATH[]).mlirCreateAsyncAsyncRuntimeRefCounting()::MlirPass +end + +function mlirRegisterAsyncAsyncRuntimeRefCounting() + @ccall (MLIR_C_PATH[]).mlirRegisterAsyncAsyncRuntimeRefCounting()::Cvoid +end + +function mlirCreateAsyncAsyncRuntimeRefCountingOpt() + @ccall (MLIR_C_PATH[]).mlirCreateAsyncAsyncRuntimeRefCountingOpt()::MlirPass +end + +function mlirRegisterAsyncAsyncRuntimeRefCountingOpt() + @ccall (MLIR_C_PATH[]).mlirRegisterAsyncAsyncRuntimeRefCountingOpt()::Cvoid +end + +function mlirCreateAsyncAsyncToAsyncRuntime() + @ccall (MLIR_C_PATH[]).mlirCreateAsyncAsyncToAsyncRuntime()::MlirPass +end + +function mlirRegisterAsyncAsyncToAsyncRuntime() + @ccall (MLIR_C_PATH[]).mlirRegisterAsyncAsyncToAsyncRuntime()::Cvoid +end + +function mlirGetDialectHandle__cf__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__cf__()::MlirDialectHandle +end + +function mlirGetDialectHandle__func__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__func__()::MlirDialectHandle +end + +""" + mlirFuncSetArgAttr(op, pos, name, attr) + +Sets the argument attribute 'name' of an argument at index 'pos'. Asserts that the operation is a FuncOp. +""" +function mlirFuncSetArgAttr(op, pos, name, attr) + @ccall (MLIR_C_PATH[]).mlirFuncSetArgAttr( + op::MlirOperation, pos::intptr_t, name::MlirStringRef, attr::MlirAttribute + )::Cvoid +end + +function mlirGetDialectHandle__gpu__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__gpu__()::MlirDialectHandle +end + +function mlirTypeIsAGPUAsyncTokenType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAGPUAsyncTokenType(type::MlirType)::Bool +end + +function mlirGPUAsyncTokenTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirGPUAsyncTokenTypeGet(ctx::MlirContext)::MlirType +end + +function mlirAttributeIsAGPUObjectAttr(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsAGPUObjectAttr(attr::MlirAttribute)::Bool +end + +function mlirGPUObjectAttrGet(mlirCtx, target, format, objectStrRef, mlirObjectProps) + @ccall (MLIR_C_PATH[]).mlirGPUObjectAttrGet( + mlirCtx::MlirContext, + target::MlirAttribute, + format::UInt32, + objectStrRef::MlirStringRef, + mlirObjectProps::MlirAttribute, + )::MlirAttribute +end + +function mlirGPUObjectAttrGetTarget(mlirObjectAttr) + @ccall (MLIR_C_PATH[]).mlirGPUObjectAttrGetTarget( + mlirObjectAttr::MlirAttribute + )::MlirAttribute +end + +function mlirGPUObjectAttrGetFormat(mlirObjectAttr) + @ccall (MLIR_C_PATH[]).mlirGPUObjectAttrGetFormat(mlirObjectAttr::MlirAttribute)::UInt32 +end + +function mlirGPUObjectAttrGetObject(mlirObjectAttr) + @ccall (MLIR_C_PATH[]).mlirGPUObjectAttrGetObject( + mlirObjectAttr::MlirAttribute + )::MlirStringRef +end + +function mlirGPUObjectAttrHasProperties(mlirObjectAttr) + @ccall (MLIR_C_PATH[]).mlirGPUObjectAttrHasProperties( + mlirObjectAttr::MlirAttribute + )::Bool +end + +function mlirGPUObjectAttrGetProperties(mlirObjectAttr) + @ccall (MLIR_C_PATH[]).mlirGPUObjectAttrGetProperties( + mlirObjectAttr::MlirAttribute + )::MlirAttribute +end + +function mlirRegisterGPUPasses() + @ccall (MLIR_C_PATH[]).mlirRegisterGPUPasses()::Cvoid +end + +function mlirCreateGPUGpuAsyncRegionPass() + @ccall (MLIR_C_PATH[]).mlirCreateGPUGpuAsyncRegionPass()::MlirPass +end + +function mlirRegisterGPUGpuAsyncRegionPass() + @ccall (MLIR_C_PATH[]).mlirRegisterGPUGpuAsyncRegionPass()::Cvoid +end + +function mlirCreateGPUGpuDecomposeMemrefsPass() + @ccall (MLIR_C_PATH[]).mlirCreateGPUGpuDecomposeMemrefsPass()::MlirPass +end + +function mlirRegisterGPUGpuDecomposeMemrefsPass() + @ccall (MLIR_C_PATH[]).mlirRegisterGPUGpuDecomposeMemrefsPass()::Cvoid +end + +function mlirCreateGPUGpuEliminateBarriers() + @ccall (MLIR_C_PATH[]).mlirCreateGPUGpuEliminateBarriers()::MlirPass +end + +function mlirRegisterGPUGpuEliminateBarriers() + @ccall (MLIR_C_PATH[]).mlirRegisterGPUGpuEliminateBarriers()::Cvoid +end + +function mlirCreateGPUGpuKernelOutlining() + @ccall (MLIR_C_PATH[]).mlirCreateGPUGpuKernelOutlining()::MlirPass +end + +function mlirRegisterGPUGpuKernelOutlining() + @ccall (MLIR_C_PATH[]).mlirRegisterGPUGpuKernelOutlining()::Cvoid +end + +function mlirCreateGPUGpuLaunchSinkIndexComputations() + @ccall (MLIR_C_PATH[]).mlirCreateGPUGpuLaunchSinkIndexComputations()::MlirPass +end + +function mlirRegisterGPUGpuLaunchSinkIndexComputations() + @ccall (MLIR_C_PATH[]).mlirRegisterGPUGpuLaunchSinkIndexComputations()::Cvoid +end + +function mlirCreateGPUGpuMapParallelLoopsPass() + @ccall (MLIR_C_PATH[]).mlirCreateGPUGpuMapParallelLoopsPass()::MlirPass +end + +function mlirRegisterGPUGpuMapParallelLoopsPass() + @ccall (MLIR_C_PATH[]).mlirRegisterGPUGpuMapParallelLoopsPass()::Cvoid +end + +function mlirCreateGPUGpuModuleToBinaryPass() + @ccall (MLIR_C_PATH[]).mlirCreateGPUGpuModuleToBinaryPass()::MlirPass +end + +function mlirRegisterGPUGpuModuleToBinaryPass() + @ccall (MLIR_C_PATH[]).mlirRegisterGPUGpuModuleToBinaryPass()::Cvoid +end + +function mlirCreateGPUGpuNVVMAttachTarget() + @ccall (MLIR_C_PATH[]).mlirCreateGPUGpuNVVMAttachTarget()::MlirPass +end + +function mlirRegisterGPUGpuNVVMAttachTarget() + @ccall (MLIR_C_PATH[]).mlirRegisterGPUGpuNVVMAttachTarget()::Cvoid +end + +function mlirCreateGPUGpuROCDLAttachTarget() + @ccall (MLIR_C_PATH[]).mlirCreateGPUGpuROCDLAttachTarget()::MlirPass +end + +function mlirRegisterGPUGpuROCDLAttachTarget() + @ccall (MLIR_C_PATH[]).mlirRegisterGPUGpuROCDLAttachTarget()::Cvoid +end + +function mlirCreateGPUGpuSPIRVAttachTarget() + @ccall (MLIR_C_PATH[]).mlirCreateGPUGpuSPIRVAttachTarget()::MlirPass +end + +function mlirRegisterGPUGpuSPIRVAttachTarget() + @ccall (MLIR_C_PATH[]).mlirRegisterGPUGpuSPIRVAttachTarget()::Cvoid +end + +function mlirGetDialectHandle__irdl__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__irdl__()::MlirDialectHandle +end + +""" + mlirLoadIRDLDialects(_module) + +Loads all IRDL dialects in the provided module, registering the dialects in the module's associated context. +""" +function mlirLoadIRDLDialects(_module) + @ccall (MLIR_C_PATH[]).mlirLoadIRDLDialects(_module::MlirModule)::MlirLogicalResult +end + +function mlirGetDialectHandle__llvm__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__llvm__()::MlirDialectHandle +end + +""" + mlirLLVMPointerTypeGet(ctx, addressSpace) + +Creates an llvm.ptr type. +""" +function mlirLLVMPointerTypeGet(ctx, addressSpace) + @ccall (MLIR_C_PATH[]).mlirLLVMPointerTypeGet( + ctx::MlirContext, addressSpace::Cuint + )::MlirType +end + +""" + mlirTypeIsALLVMPointerType(type) + +Returns `true` if the type is an LLVM dialect pointer type. +""" +function mlirTypeIsALLVMPointerType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsALLVMPointerType(type::MlirType)::Bool +end + +""" + mlirLLVMPointerTypeGetAddressSpace(pointerType) + +Returns address space of llvm.ptr +""" +function mlirLLVMPointerTypeGetAddressSpace(pointerType) + @ccall (MLIR_C_PATH[]).mlirLLVMPointerTypeGetAddressSpace(pointerType::MlirType)::Cuint +end + +""" + mlirLLVMVoidTypeGet(ctx) + +Creates an llmv.void type. +""" +function mlirLLVMVoidTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirLLVMVoidTypeGet(ctx::MlirContext)::MlirType +end + +""" + mlirLLVMArrayTypeGet(elementType, numElements) + +Creates an llvm.array type. +""" +function mlirLLVMArrayTypeGet(elementType, numElements) + @ccall (MLIR_C_PATH[]).mlirLLVMArrayTypeGet( + elementType::MlirType, numElements::Cuint + )::MlirType +end + +""" + mlirLLVMFunctionTypeGet(resultType, nArgumentTypes, argumentTypes, isVarArg) + +Creates an llvm.func type. +""" +function mlirLLVMFunctionTypeGet(resultType, nArgumentTypes, argumentTypes, isVarArg) + @ccall (MLIR_C_PATH[]).mlirLLVMFunctionTypeGet( + resultType::MlirType, + nArgumentTypes::intptr_t, + argumentTypes::Ptr{MlirType}, + isVarArg::Bool, + )::MlirType +end + +""" + mlirTypeIsALLVMStructType(type) + +Returns `true` if the type is an LLVM dialect struct type. +""" +function mlirTypeIsALLVMStructType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsALLVMStructType(type::MlirType)::Bool +end + +""" + mlirLLVMStructTypeIsLiteral(type) + +Returns `true` if the type is a literal (unnamed) LLVM struct type. +""" +function mlirLLVMStructTypeIsLiteral(type) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeIsLiteral(type::MlirType)::Bool +end + +""" + mlirLLVMStructTypeGetNumElementTypes(type) + +Returns the number of fields in the struct. Asserts if the struct is opaque or not yet initialized. +""" +function mlirLLVMStructTypeGetNumElementTypes(type) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeGetNumElementTypes(type::MlirType)::intptr_t +end + +""" + mlirLLVMStructTypeGetElementType(type, position) + +Returns the `positions`-th field of the struct. Asserts if the struct is opaque, not yet initialized or if the position is out of range. +""" +function mlirLLVMStructTypeGetElementType(type, position) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeGetElementType( + type::MlirType, position::intptr_t + )::MlirType +end + +""" + mlirLLVMStructTypeIsPacked(type) + +Returns `true` if the struct is packed. +""" +function mlirLLVMStructTypeIsPacked(type) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeIsPacked(type::MlirType)::Bool +end + +""" + mlirLLVMStructTypeGetIdentifier(type) + +Returns the identifier of the identified struct. Asserts that the struct is identified, i.e., not literal. +""" +function mlirLLVMStructTypeGetIdentifier(type) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeGetIdentifier(type::MlirType)::MlirStringRef +end + +""" + mlirLLVMStructTypeIsOpaque(type) + +Returns `true` is the struct is explicitly opaque (will not have a body) or uninitialized (will eventually have a body). +""" +function mlirLLVMStructTypeIsOpaque(type) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeIsOpaque(type::MlirType)::Bool +end + +""" + mlirLLVMStructTypeLiteralGet(ctx, nFieldTypes, fieldTypes, isPacked) + +Creates an LLVM literal (unnamed) struct type. This may assert if the fields have types not compatible with the LLVM dialect. For a graceful failure, use the checked version. +""" +function mlirLLVMStructTypeLiteralGet(ctx, nFieldTypes, fieldTypes, isPacked) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeLiteralGet( + ctx::MlirContext, nFieldTypes::intptr_t, fieldTypes::Ptr{MlirType}, isPacked::Bool + )::MlirType +end + +""" + mlirLLVMStructTypeLiteralGetChecked(loc, nFieldTypes, fieldTypes, isPacked) + +Creates an LLVM literal (unnamed) struct type if possible. Emits a diagnostic at the given location and returns null otherwise. +""" +function mlirLLVMStructTypeLiteralGetChecked(loc, nFieldTypes, fieldTypes, isPacked) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeLiteralGetChecked( + loc::MlirLocation, nFieldTypes::intptr_t, fieldTypes::Ptr{MlirType}, isPacked::Bool + )::MlirType +end + +""" + mlirLLVMStructTypeIdentifiedGet(ctx, name) + +Creates an LLVM identified struct type with no body. If a struct type with this name already exists in the context, returns that type. Use [`mlirLLVMStructTypeIdentifiedNewGet`](@ref) to create a fresh struct type, potentially renaming it. The body should be set separatelty by calling [`mlirLLVMStructTypeSetBody`](@ref), if it isn't set already. +""" +function mlirLLVMStructTypeIdentifiedGet(ctx, name) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeIdentifiedGet( + ctx::MlirContext, name::MlirStringRef + )::MlirType +end + +""" + mlirLLVMStructTypeIdentifiedNewGet(ctx, name, nFieldTypes, fieldTypes, isPacked) + +Creates an LLVM identified struct type with no body and a name starting with the given prefix. If a struct with the exact name as the given prefix already exists, appends an unspecified suffix to the name so that the name is unique in context. +""" +function mlirLLVMStructTypeIdentifiedNewGet(ctx, name, nFieldTypes, fieldTypes, isPacked) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeIdentifiedNewGet( + ctx::MlirContext, + name::MlirStringRef, + nFieldTypes::intptr_t, + fieldTypes::Ptr{MlirType}, + isPacked::Bool, + )::MlirType +end + +function mlirLLVMStructTypeOpaqueGet(ctx, name) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeOpaqueGet( + ctx::MlirContext, name::MlirStringRef + )::MlirType +end + +""" + mlirLLVMStructTypeSetBody(structType, nFieldTypes, fieldTypes, isPacked) + +Sets the body of the identified struct if it hasn't been set yet. Returns whether the operation was successful. +""" +function mlirLLVMStructTypeSetBody(structType, nFieldTypes, fieldTypes, isPacked) + @ccall (MLIR_C_PATH[]).mlirLLVMStructTypeSetBody( + structType::MlirType, + nFieldTypes::intptr_t, + fieldTypes::Ptr{MlirType}, + isPacked::Bool, + )::MlirLogicalResult +end + +@cenum MlirLLVMCConv::UInt32 begin + MlirLLVMCConvC = 0x0000000000000000 + MlirLLVMCConvFast = 0x0000000000000008 + MlirLLVMCConvCold = 0x0000000000000009 + MlirLLVMCConvGHC = 0x000000000000000a + MlirLLVMCConvHiPE = 0x000000000000000b + MlirLLVMCConvAnyReg = 0x000000000000000d + MlirLLVMCConvPreserveMost = 0x000000000000000e + MlirLLVMCConvPreserveAll = 0x000000000000000f + MlirLLVMCConvSwift = 0x0000000000000010 + MlirLLVMCConvCXX_FAST_TLS = 0x0000000000000011 + MlirLLVMCConvTail = 0x0000000000000012 + MlirLLVMCConvCFGuard_Check = 0x0000000000000013 + MlirLLVMCConvSwiftTail = 0x0000000000000014 + MlirLLVMCConvX86_StdCall = 0x0000000000000040 + MlirLLVMCConvX86_FastCall = 0x0000000000000041 + MlirLLVMCConvARM_APCS = 0x0000000000000042 + MlirLLVMCConvARM_AAPCS = 0x0000000000000043 + MlirLLVMCConvARM_AAPCS_VFP = 0x0000000000000044 + MlirLLVMCConvMSP430_INTR = 0x0000000000000045 + MlirLLVMCConvX86_ThisCall = 0x0000000000000046 + MlirLLVMCConvPTX_Kernel = 0x0000000000000047 + MlirLLVMCConvPTX_Device = 0x0000000000000048 + MlirLLVMCConvSPIR_FUNC = 0x000000000000004b + MlirLLVMCConvSPIR_KERNEL = 0x000000000000004c + MlirLLVMCConvIntel_OCL_BI = 0x000000000000004d + MlirLLVMCConvX86_64_SysV = 0x000000000000004e + MlirLLVMCConvWin64 = 0x000000000000004f + MlirLLVMCConvX86_VectorCall = 0x0000000000000050 + MlirLLVMCConvDUMMY_HHVM = 0x0000000000000051 + MlirLLVMCConvDUMMY_HHVM_C = 0x0000000000000052 + MlirLLVMCConvX86_INTR = 0x0000000000000053 + MlirLLVMCConvAVR_INTR = 0x0000000000000054 + MlirLLVMCConvAVR_BUILTIN = 0x0000000000000056 + MlirLLVMCConvAMDGPU_VS = 0x0000000000000057 + MlirLLVMCConvAMDGPU_GS = 0x0000000000000058 + MlirLLVMCConvAMDGPU_CS = 0x000000000000005a + MlirLLVMCConvAMDGPU_KERNEL = 0x000000000000005b + MlirLLVMCConvX86_RegCall = 0x000000000000005c + MlirLLVMCConvAMDGPU_HS = 0x000000000000005d + MlirLLVMCConvMSP430_BUILTIN = 0x000000000000005e + MlirLLVMCConvAMDGPU_LS = 0x000000000000005f + MlirLLVMCConvAMDGPU_ES = 0x0000000000000060 + MlirLLVMCConvAArch64_VectorCall = 0x0000000000000061 + MlirLLVMCConvAArch64_SVE_VectorCall = 0x0000000000000062 + MlirLLVMCConvWASM_EmscriptenInvoke = 0x0000000000000063 + MlirLLVMCConvAMDGPU_Gfx = 0x0000000000000064 + MlirLLVMCConvM68k_INTR = 0x0000000000000065 +end + +""" + mlirLLVMCConvAttrGet(ctx, cconv) + +Creates a LLVM CConv attribute. +""" +function mlirLLVMCConvAttrGet(ctx, cconv) + @ccall (MLIR_C_PATH[]).mlirLLVMCConvAttrGet( + ctx::MlirContext, cconv::MlirLLVMCConv + )::MlirAttribute +end + +@cenum MlirLLVMComdat::UInt32 begin + MlirLLVMComdatAny = 0x0000000000000000 + MlirLLVMComdatExactMatch = 0x0000000000000001 + MlirLLVMComdatLargest = 0x0000000000000002 + MlirLLVMComdatNoDeduplicate = 0x0000000000000003 + MlirLLVMComdatSameSize = 0x0000000000000004 +end + +""" + mlirLLVMComdatAttrGet(ctx, comdat) + +Creates a LLVM Comdat attribute. +""" +function mlirLLVMComdatAttrGet(ctx, comdat) + @ccall (MLIR_C_PATH[]).mlirLLVMComdatAttrGet( + ctx::MlirContext, comdat::MlirLLVMComdat + )::MlirAttribute +end + +@cenum MlirLLVMLinkage::UInt32 begin + MlirLLVMLinkagePrivate = 0x0000000000000000 + MlirLLVMLinkageInternal = 0x0000000000000001 + MlirLLVMLinkageAvailableExternally = 0x0000000000000002 + MlirLLVMLinkageLinkonce = 0x0000000000000003 + MlirLLVMLinkageWeak = 0x0000000000000004 + MlirLLVMLinkageCommon = 0x0000000000000005 + MlirLLVMLinkageAppending = 0x0000000000000006 + MlirLLVMLinkageExternWeak = 0x0000000000000007 + MlirLLVMLinkageLinkonceODR = 0x0000000000000008 + MlirLLVMLinkageWeakODR = 0x0000000000000009 + MlirLLVMLinkageExternal = 0x000000000000000a +end + +""" + mlirLLVMLinkageAttrGet(ctx, linkage) + +Creates a LLVM Linkage attribute. +""" +function mlirLLVMLinkageAttrGet(ctx, linkage) + @ccall (MLIR_C_PATH[]).mlirLLVMLinkageAttrGet( + ctx::MlirContext, linkage::MlirLLVMLinkage + )::MlirAttribute +end + +""" + mlirLLVMDINullTypeAttrGet(ctx) + +Creates a LLVM DINullType attribute. +""" +function mlirLLVMDINullTypeAttrGet(ctx) + @ccall (MLIR_C_PATH[]).mlirLLVMDINullTypeAttrGet(ctx::MlirContext)::MlirAttribute +end + +""" + mlirLLVMDIExpressionElemAttrGet(ctx, opcode, nArguments, arguments) + +Creates a LLVM DIExpressionElem attribute. +""" +function mlirLLVMDIExpressionElemAttrGet(ctx, opcode, nArguments, arguments) + @ccall (MLIR_C_PATH[]).mlirLLVMDIExpressionElemAttrGet( + ctx::MlirContext, opcode::Cuint, nArguments::intptr_t, arguments::Ptr{UInt64} + )::MlirAttribute +end + +""" + mlirLLVMDIExpressionAttrGet(ctx, nOperations, operations) + +Creates a LLVM DIExpression attribute. +""" +function mlirLLVMDIExpressionAttrGet(ctx, nOperations, operations) + @ccall (MLIR_C_PATH[]).mlirLLVMDIExpressionAttrGet( + ctx::MlirContext, nOperations::intptr_t, operations::Ptr{MlirAttribute} + )::MlirAttribute +end + +@cenum MlirLLVMTypeEncoding::UInt32 begin + MlirLLVMTypeEncodingAddress = 0x0000000000000001 + MlirLLVMTypeEncodingBoolean = 0x0000000000000002 + MlirLLVMTypeEncodingComplexFloat = 0x0000000000000031 + MlirLLVMTypeEncodingFloatT = 0x0000000000000004 + MlirLLVMTypeEncodingSigned = 0x0000000000000005 + MlirLLVMTypeEncodingSignedChar = 0x0000000000000006 + MlirLLVMTypeEncodingUnsigned = 0x0000000000000007 + MlirLLVMTypeEncodingUnsignedChar = 0x0000000000000008 + MlirLLVMTypeEncodingImaginaryFloat = 0x0000000000000009 + MlirLLVMTypeEncodingPackedDecimal = 0x000000000000000a + MlirLLVMTypeEncodingNumericString = 0x000000000000000b + MlirLLVMTypeEncodingEdited = 0x000000000000000c + MlirLLVMTypeEncodingSignedFixed = 0x000000000000000d + MlirLLVMTypeEncodingUnsignedFixed = 0x000000000000000e + MlirLLVMTypeEncodingDecimalFloat = 0x000000000000000f + MlirLLVMTypeEncodingUTF = 0x0000000000000010 + MlirLLVMTypeEncodingUCS = 0x0000000000000011 + MlirLLVMTypeEncodingASCII = 0x0000000000000012 + MlirLLVMTypeEncodingLoUser = 0x0000000000000080 + MlirLLVMTypeEncodingHiUser = 0x00000000000000ff +end + +""" + mlirLLVMDIBasicTypeAttrGet(ctx, tag, name, sizeInBits, encoding) + +Creates a LLVM DIBasicType attribute. +""" +function mlirLLVMDIBasicTypeAttrGet(ctx, tag, name, sizeInBits, encoding) + @ccall (MLIR_C_PATH[]).mlirLLVMDIBasicTypeAttrGet( + ctx::MlirContext, + tag::Cuint, + name::MlirAttribute, + sizeInBits::UInt64, + encoding::MlirLLVMTypeEncoding, + )::MlirAttribute +end + +""" + mlirLLVMDICompositeTypeAttrGet(ctx, tag, recId, name, file, line, scope, baseType, flags, sizeInBits, alignInBits, nElements, elements, dataLocation, rank, allocated, associated) + +Creates a LLVM DICompositeType attribute. +""" +function mlirLLVMDICompositeTypeAttrGet( + ctx, + tag, + recId, + name, + file, + line, + scope, + baseType, + flags, + sizeInBits, + alignInBits, + nElements, + elements, + dataLocation, + rank, + allocated, + associated, +) + @ccall (MLIR_C_PATH[]).mlirLLVMDICompositeTypeAttrGet( + ctx::MlirContext, + tag::Cuint, + recId::MlirAttribute, + name::MlirAttribute, + file::MlirAttribute, + line::UInt32, + scope::MlirAttribute, + baseType::MlirAttribute, + flags::Int64, + sizeInBits::UInt64, + alignInBits::UInt64, + nElements::intptr_t, + elements::Ptr{MlirAttribute}, + dataLocation::MlirAttribute, + rank::MlirAttribute, + allocated::MlirAttribute, + associated::MlirAttribute, + )::MlirAttribute +end + +""" + mlirLLVMDIDerivedTypeAttrGet(ctx, tag, name, baseType, sizeInBits, alignInBits, offsetInBits, dwarfAddressSpace, extraData) + +Creates a LLVM DIDerivedType attribute. Note that `dwarfAddressSpace` is an optional field, where [`MLIR_CAPI_DWARF_ADDRESS_SPACE_NULL`](@ref) indicates null and non-negative values indicate a value present. +""" +function mlirLLVMDIDerivedTypeAttrGet( + ctx, + tag, + name, + baseType, + sizeInBits, + alignInBits, + offsetInBits, + dwarfAddressSpace, + extraData, +) + @ccall (MLIR_C_PATH[]).mlirLLVMDIDerivedTypeAttrGet( + ctx::MlirContext, + tag::Cuint, + name::MlirAttribute, + baseType::MlirAttribute, + sizeInBits::UInt64, + alignInBits::UInt32, + offsetInBits::UInt64, + dwarfAddressSpace::Int64, + extraData::MlirAttribute, + )::MlirAttribute +end + +function mlirLLVMDIStringTypeAttrGet( + ctx, + tag, + name, + sizeInBits, + alignInBits, + stringLength, + stringLengthExp, + stringLocationExp, + encoding, +) + @ccall (MLIR_C_PATH[]).mlirLLVMDIStringTypeAttrGet( + ctx::MlirContext, + tag::Cuint, + name::MlirAttribute, + sizeInBits::UInt64, + alignInBits::UInt32, + stringLength::MlirAttribute, + stringLengthExp::MlirAttribute, + stringLocationExp::MlirAttribute, + encoding::MlirLLVMTypeEncoding, + )::MlirAttribute +end + +""" + mlirLLVMDIDerivedTypeAttrGetBaseType(diDerivedType) + +Gets the base type from a LLVM DIDerivedType attribute. +""" +function mlirLLVMDIDerivedTypeAttrGetBaseType(diDerivedType) + @ccall (MLIR_C_PATH[]).mlirLLVMDIDerivedTypeAttrGetBaseType( + diDerivedType::MlirAttribute + )::MlirAttribute +end + +""" + mlirLLVMDIFileAttrGet(ctx, name, directory) + +Creates a LLVM DIFileAttr attribute. +""" +function mlirLLVMDIFileAttrGet(ctx, name, directory) + @ccall (MLIR_C_PATH[]).mlirLLVMDIFileAttrGet( + ctx::MlirContext, name::MlirAttribute, directory::MlirAttribute + )::MlirAttribute +end + +@cenum MlirLLVMDIEmissionKind::UInt32 begin + MlirLLVMDIEmissionKindNone = 0x0000000000000000 + MlirLLVMDIEmissionKindFull = 0x0000000000000001 + MlirLLVMDIEmissionKindLineTablesOnly = 0x0000000000000002 + MlirLLVMDIEmissionKindDebugDirectivesOnly = 0x0000000000000003 +end + +@cenum MlirLLVMDINameTableKind::UInt32 begin + MlirLLVMDINameTableKindDefault = 0x0000000000000000 + MlirLLVMDINameTableKindGNU = 0x0000000000000001 + MlirLLVMDINameTableKindNone = 0x0000000000000002 + MlirLLVMDINameTableKindApple = 0x0000000000000003 +end + +""" + mlirLLVMDICompileUnitAttrGet(ctx, id, sourceLanguage, file, producer, isOptimized, emissionKind, nameTableKind) + +Creates a LLVM DICompileUnit attribute. +""" +function mlirLLVMDICompileUnitAttrGet( + ctx, id, sourceLanguage, file, producer, isOptimized, emissionKind, nameTableKind +) + @ccall (MLIR_C_PATH[]).mlirLLVMDICompileUnitAttrGet( + ctx::MlirContext, + id::MlirAttribute, + sourceLanguage::Cuint, + file::MlirAttribute, + producer::MlirAttribute, + isOptimized::Bool, + emissionKind::MlirLLVMDIEmissionKind, + nameTableKind::MlirLLVMDINameTableKind, + )::MlirAttribute +end + +""" + mlirLLVMDIFlagsAttrGet(ctx, value) + +Creates a LLVM DIFlags attribute. +""" +function mlirLLVMDIFlagsAttrGet(ctx, value) + @ccall (MLIR_C_PATH[]).mlirLLVMDIFlagsAttrGet( + ctx::MlirContext, value::UInt64 + )::MlirAttribute +end + +""" + mlirLLVMDILexicalBlockAttrGet(ctx, scope, file, line, column) + +Creates a LLVM DILexicalBlock attribute. +""" +function mlirLLVMDILexicalBlockAttrGet(ctx, scope, file, line, column) + @ccall (MLIR_C_PATH[]).mlirLLVMDILexicalBlockAttrGet( + ctx::MlirContext, + scope::MlirAttribute, + file::MlirAttribute, + line::Cuint, + column::Cuint, + )::MlirAttribute +end + +""" + mlirLLVMDILexicalBlockFileAttrGet(ctx, scope, file, discriminator) + +Creates a LLVM DILexicalBlockFile attribute. +""" +function mlirLLVMDILexicalBlockFileAttrGet(ctx, scope, file, discriminator) + @ccall (MLIR_C_PATH[]).mlirLLVMDILexicalBlockFileAttrGet( + ctx::MlirContext, scope::MlirAttribute, file::MlirAttribute, discriminator::Cuint + )::MlirAttribute +end + +""" + mlirLLVMDILocalVariableAttrGet(ctx, scope, name, diFile, line, arg, alignInBits, diType) + +Creates a LLVM DILocalVariableAttr attribute. +""" +function mlirLLVMDILocalVariableAttrGet( + ctx, scope, name, diFile, line, arg, alignInBits, diType +) + @ccall (MLIR_C_PATH[]).mlirLLVMDILocalVariableAttrGet( + ctx::MlirContext, + scope::MlirAttribute, + name::MlirAttribute, + diFile::MlirAttribute, + line::Cuint, + arg::Cuint, + alignInBits::Cuint, + diType::MlirAttribute, + )::MlirAttribute +end + +""" + mlirLLVMDISubprogramAttrGet(ctx, id, compileUnit, scope, name, linkageName, file, line, scopeLine, subprogramFlags, type) + +Creates a LLVM DISubprogramAttr attribute. +""" +function mlirLLVMDISubprogramAttrGet( + ctx, + id, + compileUnit, + scope, + name, + linkageName, + file, + line, + scopeLine, + subprogramFlags, + type, +) + @ccall (MLIR_C_PATH[]).mlirLLVMDISubprogramAttrGet( + ctx::MlirContext, + id::MlirAttribute, + compileUnit::MlirAttribute, + scope::MlirAttribute, + name::MlirAttribute, + linkageName::MlirAttribute, + file::MlirAttribute, + line::Cuint, + scopeLine::Cuint, + subprogramFlags::UInt64, + type::MlirAttribute, + )::MlirAttribute +end + +""" + mlirLLVMDISubprogramAttrGetScope(diSubprogram) + +Gets the scope from this DISubprogramAttr. +""" +function mlirLLVMDISubprogramAttrGetScope(diSubprogram) + @ccall (MLIR_C_PATH[]).mlirLLVMDISubprogramAttrGetScope( + diSubprogram::MlirAttribute + )::MlirAttribute +end + +""" + mlirLLVMDISubprogramAttrGetLine(diSubprogram) + +Gets the line from this DISubprogramAttr. +""" +function mlirLLVMDISubprogramAttrGetLine(diSubprogram) + @ccall (MLIR_C_PATH[]).mlirLLVMDISubprogramAttrGetLine( + diSubprogram::MlirAttribute + )::Cuint +end + +""" + mlirLLVMDISubprogramAttrGetScopeLine(diSubprogram) + +Gets the scope line from this DISubprogram. +""" +function mlirLLVMDISubprogramAttrGetScopeLine(diSubprogram) + @ccall (MLIR_C_PATH[]).mlirLLVMDISubprogramAttrGetScopeLine( + diSubprogram::MlirAttribute + )::Cuint +end + +""" + mlirLLVMDISubprogramAttrGetCompileUnit(diSubprogram) + +Gets the compile unit from this DISubprogram. +""" +function mlirLLVMDISubprogramAttrGetCompileUnit(diSubprogram) + @ccall (MLIR_C_PATH[]).mlirLLVMDISubprogramAttrGetCompileUnit( + diSubprogram::MlirAttribute + )::MlirAttribute +end + +""" + mlirLLVMDISubprogramAttrGetFile(diSubprogram) + +Gets the file from this DISubprogramAttr. +""" +function mlirLLVMDISubprogramAttrGetFile(diSubprogram) + @ccall (MLIR_C_PATH[]).mlirLLVMDISubprogramAttrGetFile( + diSubprogram::MlirAttribute + )::MlirAttribute +end + +""" + mlirLLVMDISubprogramAttrGetType(diSubprogram) + +Gets the type from this DISubprogramAttr. +""" +function mlirLLVMDISubprogramAttrGetType(diSubprogram) + @ccall (MLIR_C_PATH[]).mlirLLVMDISubprogramAttrGetType( + diSubprogram::MlirAttribute + )::MlirAttribute +end + +""" + mlirLLVMDISubroutineTypeAttrGet(ctx, callingConvention, nTypes, types) + +Creates a LLVM DISubroutineTypeAttr attribute. +""" +function mlirLLVMDISubroutineTypeAttrGet(ctx, callingConvention, nTypes, types) + @ccall (MLIR_C_PATH[]).mlirLLVMDISubroutineTypeAttrGet( + ctx::MlirContext, + callingConvention::Cuint, + nTypes::intptr_t, + types::Ptr{MlirAttribute}, + )::MlirAttribute +end + +""" + mlirLLVMDIModuleAttrGet(ctx, file, scope, name, configMacros, includePath, apinotes, line, isDecl) + +Creates a LLVM DIModuleAttr attribute. +""" +function mlirLLVMDIModuleAttrGet( + ctx, file, scope, name, configMacros, includePath, apinotes, line, isDecl +) + @ccall (MLIR_C_PATH[]).mlirLLVMDIModuleAttrGet( + ctx::MlirContext, + file::MlirAttribute, + scope::MlirAttribute, + name::MlirAttribute, + configMacros::MlirAttribute, + includePath::MlirAttribute, + apinotes::MlirAttribute, + line::Cuint, + isDecl::Bool, + )::MlirAttribute +end + +""" + mlirLLVMDIModuleAttrGetScope(diModule) + +Gets the scope of this DIModuleAttr. +""" +function mlirLLVMDIModuleAttrGetScope(diModule) + @ccall (MLIR_C_PATH[]).mlirLLVMDIModuleAttrGetScope( + diModule::MlirAttribute + )::MlirAttribute +end + +""" + mlirLinalgFillBuiltinNamedOpRegion(mlirOp) + +Apply the special region builder for the builtin named Linalg op. Assert that `mlirOp` is a builtin named Linalg op. +""" +function mlirLinalgFillBuiltinNamedOpRegion(mlirOp) + @ccall (MLIR_C_PATH[]).mlirLinalgFillBuiltinNamedOpRegion(mlirOp::MlirOperation)::Cvoid +end + +function mlirGetDialectHandle__linalg__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__linalg__()::MlirDialectHandle +end + +function mlirRegisterLinalgPasses() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgPasses()::Cvoid +end + +function mlirCreateLinalgConvertElementwiseToLinalgPass() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgConvertElementwiseToLinalgPass()::MlirPass +end + +function mlirRegisterLinalgConvertElementwiseToLinalgPass() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgConvertElementwiseToLinalgPass()::Cvoid +end + +function mlirCreateLinalgConvertLinalgToAffineLoopsPass() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgConvertLinalgToAffineLoopsPass()::MlirPass +end + +function mlirRegisterLinalgConvertLinalgToAffineLoopsPass() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgConvertLinalgToAffineLoopsPass()::Cvoid +end + +function mlirCreateLinalgConvertLinalgToLoopsPass() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgConvertLinalgToLoopsPass()::MlirPass +end + +function mlirRegisterLinalgConvertLinalgToLoopsPass() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgConvertLinalgToLoopsPass()::Cvoid +end + +function mlirCreateLinalgConvertLinalgToParallelLoopsPass() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgConvertLinalgToParallelLoopsPass()::MlirPass +end + +function mlirRegisterLinalgConvertLinalgToParallelLoopsPass() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgConvertLinalgToParallelLoopsPass()::Cvoid +end + +function mlirCreateLinalgLinalgBlockPackMatmul() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgLinalgBlockPackMatmul()::MlirPass +end + +function mlirRegisterLinalgLinalgBlockPackMatmul() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgLinalgBlockPackMatmul()::Cvoid +end + +function mlirCreateLinalgLinalgDetensorizePass() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgLinalgDetensorizePass()::MlirPass +end + +function mlirRegisterLinalgLinalgDetensorizePass() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgLinalgDetensorizePass()::Cvoid +end + +function mlirCreateLinalgLinalgElementwiseOpFusionPass() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgLinalgElementwiseOpFusionPass()::MlirPass +end + +function mlirRegisterLinalgLinalgElementwiseOpFusionPass() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgLinalgElementwiseOpFusionPass()::Cvoid +end + +function mlirCreateLinalgLinalgFoldUnitExtentDimsPass() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgLinalgFoldUnitExtentDimsPass()::MlirPass +end + +function mlirRegisterLinalgLinalgFoldUnitExtentDimsPass() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgLinalgFoldUnitExtentDimsPass()::Cvoid +end + +function mlirCreateLinalgLinalgGeneralizeNamedOpsPass() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgLinalgGeneralizeNamedOpsPass()::MlirPass +end + +function mlirRegisterLinalgLinalgGeneralizeNamedOpsPass() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgLinalgGeneralizeNamedOpsPass()::Cvoid +end + +function mlirCreateLinalgLinalgInlineScalarOperandsPass() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgLinalgInlineScalarOperandsPass()::MlirPass +end + +function mlirRegisterLinalgLinalgInlineScalarOperandsPass() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgLinalgInlineScalarOperandsPass()::Cvoid +end + +function mlirCreateLinalgLinalgNamedOpConversionPass() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgLinalgNamedOpConversionPass()::MlirPass +end + +function mlirRegisterLinalgLinalgNamedOpConversionPass() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgLinalgNamedOpConversionPass()::Cvoid +end + +function mlirCreateLinalgLinalgSpecializeGenericOpsPass() + @ccall (MLIR_C_PATH[]).mlirCreateLinalgLinalgSpecializeGenericOpsPass()::MlirPass +end + +function mlirRegisterLinalgLinalgSpecializeGenericOpsPass() + @ccall (MLIR_C_PATH[]).mlirRegisterLinalgLinalgSpecializeGenericOpsPass()::Cvoid +end + +function mlirGetDialectHandle__ml_program__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__ml_program__()::MlirDialectHandle +end + +function mlirGetDialectHandle__math__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__math__()::MlirDialectHandle +end + +function mlirGetDialectHandle__memref__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__memref__()::MlirDialectHandle +end + +function mlirGetDialectHandle__nvgpu__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__nvgpu__()::MlirDialectHandle +end + +function mlirTypeIsANVGPUTensorMapDescriptorType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsANVGPUTensorMapDescriptorType(type::MlirType)::Bool +end + +function mlirNVGPUTensorMapDescriptorTypeGet( + ctx, tensorMemrefType, swizzle, l2promo, oobFill, interleave +) + @ccall (MLIR_C_PATH[]).mlirNVGPUTensorMapDescriptorTypeGet( + ctx::MlirContext, + tensorMemrefType::MlirType, + swizzle::Cint, + l2promo::Cint, + oobFill::Cint, + interleave::Cint, + )::MlirType +end + +function mlirGetDialectHandle__nvvm__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__nvvm__()::MlirDialectHandle +end + +function mlirGetDialectHandle__omp__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__omp__()::MlirDialectHandle +end + +function mlirGetDialectHandle__pdl__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__pdl__()::MlirDialectHandle +end + +function mlirTypeIsAPDLType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAPDLType(type::MlirType)::Bool +end + +function mlirTypeIsAPDLAttributeType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAPDLAttributeType(type::MlirType)::Bool +end + +function mlirPDLAttributeTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirPDLAttributeTypeGet(ctx::MlirContext)::MlirType +end + +function mlirTypeIsAPDLOperationType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAPDLOperationType(type::MlirType)::Bool +end + +function mlirPDLOperationTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirPDLOperationTypeGet(ctx::MlirContext)::MlirType +end + +function mlirTypeIsAPDLRangeType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAPDLRangeType(type::MlirType)::Bool +end + +function mlirPDLRangeTypeGet(elementType) + @ccall (MLIR_C_PATH[]).mlirPDLRangeTypeGet(elementType::MlirType)::MlirType +end + +function mlirPDLRangeTypeGetElementType(type) + @ccall (MLIR_C_PATH[]).mlirPDLRangeTypeGetElementType(type::MlirType)::MlirType +end + +function mlirTypeIsAPDLTypeType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAPDLTypeType(type::MlirType)::Bool +end + +function mlirPDLTypeTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirPDLTypeTypeGet(ctx::MlirContext)::MlirType +end + +function mlirTypeIsAPDLValueType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAPDLValueType(type::MlirType)::Bool +end + +function mlirPDLValueTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirPDLValueTypeGet(ctx::MlirContext)::MlirType +end + +function mlirGetDialectHandle__quant__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__quant__()::MlirDialectHandle +end + +""" + mlirTypeIsAQuantizedType(type) + +Returns `true` if the given type is a quantization dialect type. +""" +function mlirTypeIsAQuantizedType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAQuantizedType(type::MlirType)::Bool +end + +""" + mlirQuantizedTypeGetSignedFlag() + +Returns the bit flag used to indicate signedness of a quantized type. +""" +function mlirQuantizedTypeGetSignedFlag() + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeGetSignedFlag()::Cuint +end + +""" + mlirQuantizedTypeGetDefaultMinimumForInteger(isSigned, integralWidth) + +Returns the minimum possible value stored by a quantized type. +""" +function mlirQuantizedTypeGetDefaultMinimumForInteger(isSigned, integralWidth) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeGetDefaultMinimumForInteger( + isSigned::Bool, integralWidth::Cuint + )::Int64 +end + +""" + mlirQuantizedTypeGetDefaultMaximumForInteger(isSigned, integralWidth) + +Returns the maximum possible value stored by a quantized type. +""" +function mlirQuantizedTypeGetDefaultMaximumForInteger(isSigned, integralWidth) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeGetDefaultMaximumForInteger( + isSigned::Bool, integralWidth::Cuint + )::Int64 +end + +""" + mlirQuantizedTypeGetExpressedType(type) + +Gets the original type approximated by the given quantized type. +""" +function mlirQuantizedTypeGetExpressedType(type) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeGetExpressedType(type::MlirType)::MlirType +end + +""" + mlirQuantizedTypeGetFlags(type) + +Gets the flags associated with the given quantized type. +""" +function mlirQuantizedTypeGetFlags(type) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeGetFlags(type::MlirType)::Cuint +end + +""" + mlirQuantizedTypeIsSigned(type) + +Returns `true` if the given type is signed, `false` otherwise. +""" +function mlirQuantizedTypeIsSigned(type) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeIsSigned(type::MlirType)::Bool +end + +""" + mlirQuantizedTypeGetStorageType(type) + +Returns the underlying type used to store the values. +""" +function mlirQuantizedTypeGetStorageType(type) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeGetStorageType(type::MlirType)::MlirType +end + +""" + mlirQuantizedTypeGetStorageTypeMin(type) + +Returns the minimum value that the storage type of the given quantized type can take. +""" +function mlirQuantizedTypeGetStorageTypeMin(type) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeGetStorageTypeMin(type::MlirType)::Int64 +end + +""" + mlirQuantizedTypeGetStorageTypeMax(type) + +Returns the maximum value that the storage type of the given quantized type can take. +""" +function mlirQuantizedTypeGetStorageTypeMax(type) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeGetStorageTypeMax(type::MlirType)::Int64 +end + +""" + mlirQuantizedTypeGetStorageTypeIntegralWidth(type) + +Returns the integral bitwidth that the storage type of the given quantized type can represent exactly. +""" +function mlirQuantizedTypeGetStorageTypeIntegralWidth(type) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeGetStorageTypeIntegralWidth( + type::MlirType + )::Cuint +end + +""" + mlirQuantizedTypeIsCompatibleExpressedType(type, candidate) + +Returns `true` if the `candidate` type is compatible with the given quantized `type`. +""" +function mlirQuantizedTypeIsCompatibleExpressedType(type, candidate) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeIsCompatibleExpressedType( + type::MlirType, candidate::MlirType + )::Bool +end + +""" + mlirQuantizedTypeGetQuantizedElementType(type) + +Returns the element type of the given quantized type as another quantized type. +""" +function mlirQuantizedTypeGetQuantizedElementType(type) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeGetQuantizedElementType( + type::MlirType + )::MlirType +end + +""" + mlirQuantizedTypeCastFromStorageType(type, candidate) + +Casts from a type based on the storage type of the given type to a corresponding type based on the given type. Returns a null type if the cast is not valid. +""" +function mlirQuantizedTypeCastFromStorageType(type, candidate) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeCastFromStorageType( + type::MlirType, candidate::MlirType + )::MlirType +end + +""" + mlirQuantizedTypeCastToStorageType(type) + +Casts from a type based on a quantized type to a corresponding typed based on the storage type. Returns a null type if the cast is not valid. +""" +function mlirQuantizedTypeCastToStorageType(type) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeCastToStorageType(type::MlirType)::MlirType +end + +""" + mlirQuantizedTypeCastFromExpressedType(type, candidate) + +Casts from a type based on the expressed type of the given type to a corresponding type based on the given type. Returns a null type if the cast is not valid. +""" +function mlirQuantizedTypeCastFromExpressedType(type, candidate) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeCastFromExpressedType( + type::MlirType, candidate::MlirType + )::MlirType +end + +""" + mlirQuantizedTypeCastToExpressedType(type) + +Casts from a type based on a quantized type to a corresponding typed based on the expressed type. Returns a null type if the cast is not valid. +""" +function mlirQuantizedTypeCastToExpressedType(type) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeCastToExpressedType(type::MlirType)::MlirType +end + +""" + mlirQuantizedTypeCastExpressedToStorageType(type, candidate) + +Casts from a type based on the expressed type of the given quantized type to equivalent type based on storage type of the same quantized type. +""" +function mlirQuantizedTypeCastExpressedToStorageType(type, candidate) + @ccall (MLIR_C_PATH[]).mlirQuantizedTypeCastExpressedToStorageType( + type::MlirType, candidate::MlirType + )::MlirType +end + +""" + mlirTypeIsAAnyQuantizedType(type) + +Returns `true` if the given type is an AnyQuantizedType. +""" +function mlirTypeIsAAnyQuantizedType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAAnyQuantizedType(type::MlirType)::Bool +end + +""" + mlirAnyQuantizedTypeGet(flags, storageType, expressedType, storageTypeMin, storageTypeMax) + +Creates an instance of AnyQuantizedType with the given parameters in the same context as `storageType` and returns it. The instance is owned by the context. +""" +function mlirAnyQuantizedTypeGet( + flags, storageType, expressedType, storageTypeMin, storageTypeMax +) + @ccall (MLIR_C_PATH[]).mlirAnyQuantizedTypeGet( + flags::Cuint, + storageType::MlirType, + expressedType::MlirType, + storageTypeMin::Int64, + storageTypeMax::Int64, + )::MlirType +end + +""" + mlirTypeIsAUniformQuantizedType(type) + +Returns `true` if the given type is a UniformQuantizedType. +""" +function mlirTypeIsAUniformQuantizedType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAUniformQuantizedType(type::MlirType)::Bool +end + +""" + mlirUniformQuantizedTypeGet(flags, storageType, expressedType, scale, zeroPoint, storageTypeMin, storageTypeMax) + +Creates an instance of UniformQuantizedType with the given parameters in the same context as `storageType` and returns it. The instance is owned by the context. +""" +function mlirUniformQuantizedTypeGet( + flags, storageType, expressedType, scale, zeroPoint, storageTypeMin, storageTypeMax +) + @ccall (MLIR_C_PATH[]).mlirUniformQuantizedTypeGet( + flags::Cuint, + storageType::MlirType, + expressedType::MlirType, + scale::Cdouble, + zeroPoint::Int64, + storageTypeMin::Int64, + storageTypeMax::Int64, + )::MlirType +end + +""" + mlirUniformQuantizedTypeGetScale(type) + +Returns the scale of the given uniform quantized type. +""" +function mlirUniformQuantizedTypeGetScale(type) + @ccall (MLIR_C_PATH[]).mlirUniformQuantizedTypeGetScale(type::MlirType)::Cdouble +end + +""" + mlirUniformQuantizedTypeGetZeroPoint(type) + +Returns the zero point of the given uniform quantized type. +""" +function mlirUniformQuantizedTypeGetZeroPoint(type) + @ccall (MLIR_C_PATH[]).mlirUniformQuantizedTypeGetZeroPoint(type::MlirType)::Int64 +end + +""" + mlirUniformQuantizedTypeIsFixedPoint(type) + +Returns `true` if the given uniform quantized type is fixed-point. +""" +function mlirUniformQuantizedTypeIsFixedPoint(type) + @ccall (MLIR_C_PATH[]).mlirUniformQuantizedTypeIsFixedPoint(type::MlirType)::Bool +end + +""" + mlirTypeIsAUniformQuantizedPerAxisType(type) + +Returns `true` if the given type is a UniformQuantizedPerAxisType. +""" +function mlirTypeIsAUniformQuantizedPerAxisType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsAUniformQuantizedPerAxisType(type::MlirType)::Bool +end + +""" + mlirUniformQuantizedPerAxisTypeGet(flags, storageType, expressedType, nDims, scales, zeroPoints, quantizedDimension, storageTypeMin, storageTypeMax) + +Creates an instance of UniformQuantizedPerAxisType with the given parameters in the same context as `storageType` and returns it. `scales` and `zeroPoints` point to `nDims` number of elements. The instance is owned by the context. +""" +function mlirUniformQuantizedPerAxisTypeGet( + flags, + storageType, + expressedType, + nDims, + scales, + zeroPoints, + quantizedDimension, + storageTypeMin, + storageTypeMax, +) + @ccall (MLIR_C_PATH[]).mlirUniformQuantizedPerAxisTypeGet( + flags::Cuint, + storageType::MlirType, + expressedType::MlirType, + nDims::intptr_t, + scales::Ptr{Cdouble}, + zeroPoints::Ptr{Int64}, + quantizedDimension::Int32, + storageTypeMin::Int64, + storageTypeMax::Int64, + )::MlirType +end + +""" + mlirUniformQuantizedPerAxisTypeGetNumDims(type) + +Returns the number of axes in the given quantized per-axis type. +""" +function mlirUniformQuantizedPerAxisTypeGetNumDims(type) + @ccall (MLIR_C_PATH[]).mlirUniformQuantizedPerAxisTypeGetNumDims( + type::MlirType + )::intptr_t +end + +""" + mlirUniformQuantizedPerAxisTypeGetScale(type, pos) + +Returns `pos`-th scale of the given quantized per-axis type. +""" +function mlirUniformQuantizedPerAxisTypeGetScale(type, pos) + @ccall (MLIR_C_PATH[]).mlirUniformQuantizedPerAxisTypeGetScale( + type::MlirType, pos::intptr_t + )::Cdouble +end + +""" + mlirUniformQuantizedPerAxisTypeGetZeroPoint(type, pos) + +Returns `pos`-th zero point of the given quantized per-axis type. +""" +function mlirUniformQuantizedPerAxisTypeGetZeroPoint(type, pos) + @ccall (MLIR_C_PATH[]).mlirUniformQuantizedPerAxisTypeGetZeroPoint( + type::MlirType, pos::intptr_t + )::Int64 +end + +""" + mlirUniformQuantizedPerAxisTypeGetQuantizedDimension(type) + +Returns the index of the quantized dimension in the given quantized per-axis type. +""" +function mlirUniformQuantizedPerAxisTypeGetQuantizedDimension(type) + @ccall (MLIR_C_PATH[]).mlirUniformQuantizedPerAxisTypeGetQuantizedDimension( + type::MlirType + )::Int32 +end + +""" + mlirUniformQuantizedPerAxisTypeIsFixedPoint(type) + +Returns `true` if the given uniform quantized per-axis type is fixed-point. +""" +function mlirUniformQuantizedPerAxisTypeIsFixedPoint(type) + @ccall (MLIR_C_PATH[]).mlirUniformQuantizedPerAxisTypeIsFixedPoint(type::MlirType)::Bool +end + +""" + mlirTypeIsACalibratedQuantizedType(type) + +Returns `true` if the given type is a CalibratedQuantizedType. +""" +function mlirTypeIsACalibratedQuantizedType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsACalibratedQuantizedType(type::MlirType)::Bool +end + +""" + mlirCalibratedQuantizedTypeGet(expressedType, min, max) + +Creates an instance of CalibratedQuantizedType with the given parameters in the same context as `expressedType` and returns it. The instance is owned by the context. +""" +function mlirCalibratedQuantizedTypeGet(expressedType, min, max) + @ccall (MLIR_C_PATH[]).mlirCalibratedQuantizedTypeGet( + expressedType::MlirType, min::Cdouble, max::Cdouble + )::MlirType +end + +""" + mlirCalibratedQuantizedTypeGetMin(type) + +Returns the min value of the given calibrated quantized type. +""" +function mlirCalibratedQuantizedTypeGetMin(type) + @ccall (MLIR_C_PATH[]).mlirCalibratedQuantizedTypeGetMin(type::MlirType)::Cdouble +end + +""" + mlirCalibratedQuantizedTypeGetMax(type) + +Returns the max value of the given calibrated quantized type. +""" +function mlirCalibratedQuantizedTypeGetMax(type) + @ccall (MLIR_C_PATH[]).mlirCalibratedQuantizedTypeGetMax(type::MlirType)::Cdouble +end + +function mlirGetDialectHandle__rocdl__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__rocdl__()::MlirDialectHandle +end + +function mlirGetDialectHandle__scf__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__scf__()::MlirDialectHandle +end + +function mlirGetDialectHandle__spirv__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__spirv__()::MlirDialectHandle +end + +function mlirGetDialectHandle__shape__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__shape__()::MlirDialectHandle +end + +function mlirGetDialectHandle__sparse_tensor__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__sparse_tensor__()::MlirDialectHandle +end + +""" +Dimension level types (and properties) that define sparse tensors. See the documentation in SparseTensorAttrDefs.td for their meaning. + +These correspond to SparseTensorEncodingAttr::LevelType in the C++ API. If updating, keep them in sync and update the static\\_assert in the impl file. +""" +const MlirSparseTensorLevelType = UInt64 + +@cenum MlirSparseTensorLevelFormat::UInt32 begin + MLIR_SPARSE_TENSOR_LEVEL_DENSE = 0x0000000000010000 + MLIR_SPARSE_TENSOR_LEVEL_BATCH = 0x0000000000020000 + MLIR_SPARSE_TENSOR_LEVEL_COMPRESSED = 0x0000000000040000 + MLIR_SPARSE_TENSOR_LEVEL_SINGLETON = 0x0000000000080000 + MLIR_SPARSE_TENSOR_LEVEL_LOOSE_COMPRESSED = 0x0000000000100000 + MLIR_SPARSE_TENSOR_LEVEL_N_OUT_OF_M = 0x0000000000200000 +end + +@cenum MlirSparseTensorLevelPropertyNondefault::UInt32 begin + MLIR_SPARSE_PROPERTY_NON_UNIQUE = 0x0000000000000001 + MLIR_SPARSE_PROPERTY_NON_ORDERED = 0x0000000000000002 +end + +""" + mlirAttributeIsASparseTensorEncodingAttr(attr) + +Checks whether the given attribute is a `sparse\\_tensor.encoding` attribute. +""" +function mlirAttributeIsASparseTensorEncodingAttr(attr) + @ccall (MLIR_C_PATH[]).mlirAttributeIsASparseTensorEncodingAttr( + attr::MlirAttribute + )::Bool +end + +""" + mlirSparseTensorEncodingAttrGet(ctx, lvlRank, lvlTypes, dimToLvl, lvlTodim, posWidth, crdWidth, explicitVal, implicitVal) + +Creates a `sparse\\_tensor.encoding` attribute with the given parameters. +""" +function mlirSparseTensorEncodingAttrGet( + ctx, lvlRank, lvlTypes, dimToLvl, lvlTodim, posWidth, crdWidth, explicitVal, implicitVal +) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrGet( + ctx::MlirContext, + lvlRank::intptr_t, + lvlTypes::Ptr{MlirSparseTensorLevelType}, + dimToLvl::MlirAffineMap, + lvlTodim::MlirAffineMap, + posWidth::Cint, + crdWidth::Cint, + explicitVal::MlirAttribute, + implicitVal::MlirAttribute, + )::MlirAttribute +end + +""" + mlirSparseTensorEncodingGetLvlRank(attr) + +Returns the level-rank of the `sparse\\_tensor.encoding` attribute. +""" +function mlirSparseTensorEncodingGetLvlRank(attr) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingGetLvlRank(attr::MlirAttribute)::intptr_t +end + +""" + mlirSparseTensorEncodingAttrGetLvlType(attr, lvl) + +Returns a specified level-type of the `sparse\\_tensor.encoding` attribute. +""" +function mlirSparseTensorEncodingAttrGetLvlType(attr, lvl) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrGetLvlType( + attr::MlirAttribute, lvl::intptr_t + )::MlirSparseTensorLevelType +end + +""" + mlirSparseTensorEncodingAttrGetLvlFmt(attr, lvl) + +Returns a specified level-format of the `sparse\\_tensor.encoding` attribute. +""" +function mlirSparseTensorEncodingAttrGetLvlFmt(attr, lvl) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrGetLvlFmt( + attr::MlirAttribute, lvl::intptr_t + )::MlirSparseTensorLevelFormat +end + +""" + mlirSparseTensorEncodingAttrGetDimToLvl(attr) + +Returns the dimension-to-level mapping of the `sparse\\_tensor.encoding` attribute. +""" +function mlirSparseTensorEncodingAttrGetDimToLvl(attr) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrGetDimToLvl( + attr::MlirAttribute + )::MlirAffineMap +end + +""" + mlirSparseTensorEncodingAttrGetLvlToDim(attr) + +Returns the level-to-dimension mapping of the `sparse\\_tensor.encoding` attribute. +""" +function mlirSparseTensorEncodingAttrGetLvlToDim(attr) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrGetLvlToDim( + attr::MlirAttribute + )::MlirAffineMap +end + +""" + mlirSparseTensorEncodingAttrGetPosWidth(attr) + +Returns the position bitwidth of the `sparse\\_tensor.encoding` attribute. +""" +function mlirSparseTensorEncodingAttrGetPosWidth(attr) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrGetPosWidth( + attr::MlirAttribute + )::Cint +end + +""" + mlirSparseTensorEncodingAttrGetCrdWidth(attr) + +Returns the coordinate bitwidth of the `sparse\\_tensor.encoding` attribute. +""" +function mlirSparseTensorEncodingAttrGetCrdWidth(attr) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrGetCrdWidth( + attr::MlirAttribute + )::Cint +end + +""" + mlirSparseTensorEncodingAttrGetExplicitVal(attr) + +Returns the explicit value of the `sparse\\_tensor.encoding` attribute. +""" +function mlirSparseTensorEncodingAttrGetExplicitVal(attr) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrGetExplicitVal( + attr::MlirAttribute + )::MlirAttribute +end + +""" + mlirSparseTensorEncodingAttrGetImplicitVal(attr) + +Returns the implicit value of the `sparse\\_tensor.encoding` attribute. +""" +function mlirSparseTensorEncodingAttrGetImplicitVal(attr) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrGetImplicitVal( + attr::MlirAttribute + )::MlirAttribute +end + +function mlirSparseTensorEncodingAttrGetStructuredN(lvlType) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrGetStructuredN( + lvlType::MlirSparseTensorLevelType + )::Cuint +end + +function mlirSparseTensorEncodingAttrGetStructuredM(lvlType) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrGetStructuredM( + lvlType::MlirSparseTensorLevelType + )::Cuint +end + +function mlirSparseTensorEncodingAttrBuildLvlType(lvlFmt, properties, propSize, n, m) + @ccall (MLIR_C_PATH[]).mlirSparseTensorEncodingAttrBuildLvlType( + lvlFmt::MlirSparseTensorLevelFormat, + properties::Ptr{MlirSparseTensorLevelPropertyNondefault}, + propSize::Cuint, + n::Cuint, + m::Cuint, + )::MlirSparseTensorLevelType +end + +function mlirRegisterSparseTensorPasses() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorPasses()::Cvoid +end + +function mlirCreateSparseTensorLowerForeachToSCF() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorLowerForeachToSCF()::MlirPass +end + +function mlirRegisterSparseTensorLowerForeachToSCF() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorLowerForeachToSCF()::Cvoid +end + +function mlirCreateSparseTensorLowerSparseIterationToSCF() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorLowerSparseIterationToSCF()::MlirPass +end + +function mlirRegisterSparseTensorLowerSparseIterationToSCF() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorLowerSparseIterationToSCF()::Cvoid +end + +function mlirCreateSparseTensorLowerSparseOpsToForeach() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorLowerSparseOpsToForeach()::MlirPass +end + +function mlirRegisterSparseTensorLowerSparseOpsToForeach() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorLowerSparseOpsToForeach()::Cvoid +end + +function mlirCreateSparseTensorPreSparsificationRewrite() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorPreSparsificationRewrite()::MlirPass +end + +function mlirRegisterSparseTensorPreSparsificationRewrite() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorPreSparsificationRewrite()::Cvoid +end + +function mlirCreateSparseTensorSparseAssembler() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorSparseAssembler()::MlirPass +end + +function mlirRegisterSparseTensorSparseAssembler() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorSparseAssembler()::Cvoid +end + +function mlirCreateSparseTensorSparseBufferRewrite() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorSparseBufferRewrite()::MlirPass +end + +function mlirRegisterSparseTensorSparseBufferRewrite() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorSparseBufferRewrite()::Cvoid +end + +function mlirCreateSparseTensorSparseGPUCodegen() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorSparseGPUCodegen()::MlirPass +end + +function mlirRegisterSparseTensorSparseGPUCodegen() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorSparseGPUCodegen()::Cvoid +end + +function mlirCreateSparseTensorSparseReinterpretMap() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorSparseReinterpretMap()::MlirPass +end + +function mlirRegisterSparseTensorSparseReinterpretMap() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorSparseReinterpretMap()::Cvoid +end + +function mlirCreateSparseTensorSparseSpaceCollapse() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorSparseSpaceCollapse()::MlirPass +end + +function mlirRegisterSparseTensorSparseSpaceCollapse() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorSparseSpaceCollapse()::Cvoid +end + +function mlirCreateSparseTensorSparseTensorCodegen() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorSparseTensorCodegen()::MlirPass +end + +function mlirRegisterSparseTensorSparseTensorCodegen() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorSparseTensorCodegen()::Cvoid +end + +function mlirCreateSparseTensorSparseTensorConversionPass() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorSparseTensorConversionPass()::MlirPass +end + +function mlirRegisterSparseTensorSparseTensorConversionPass() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorSparseTensorConversionPass()::Cvoid +end + +function mlirCreateSparseTensorSparseVectorization() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorSparseVectorization()::MlirPass +end + +function mlirRegisterSparseTensorSparseVectorization() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorSparseVectorization()::Cvoid +end + +function mlirCreateSparseTensorSparsificationAndBufferization() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorSparsificationAndBufferization()::MlirPass +end + +function mlirRegisterSparseTensorSparsificationAndBufferization() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorSparsificationAndBufferization()::Cvoid +end + +function mlirCreateSparseTensorSparsificationPass() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorSparsificationPass()::MlirPass +end + +function mlirRegisterSparseTensorSparsificationPass() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorSparsificationPass()::Cvoid +end + +function mlirCreateSparseTensorStageSparseOperations() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorStageSparseOperations()::MlirPass +end + +function mlirRegisterSparseTensorStageSparseOperations() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorStageSparseOperations()::Cvoid +end + +function mlirCreateSparseTensorStorageSpecifierToLLVM() + @ccall (MLIR_C_PATH[]).mlirCreateSparseTensorStorageSpecifierToLLVM()::MlirPass +end + +function mlirRegisterSparseTensorStorageSpecifierToLLVM() + @ccall (MLIR_C_PATH[]).mlirRegisterSparseTensorStorageSpecifierToLLVM()::Cvoid +end + +function mlirGetDialectHandle__tensor__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__tensor__()::MlirDialectHandle +end + +function mlirGetDialectHandle__transform__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__transform__()::MlirDialectHandle +end + +function mlirTypeIsATransformAnyOpType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsATransformAnyOpType(type::MlirType)::Bool +end + +function mlirTransformAnyOpTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirTransformAnyOpTypeGetTypeID()::MlirTypeID +end + +function mlirTransformAnyOpTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirTransformAnyOpTypeGet(ctx::MlirContext)::MlirType +end + +function mlirTypeIsATransformAnyParamType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsATransformAnyParamType(type::MlirType)::Bool +end + +function mlirTransformAnyParamTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirTransformAnyParamTypeGetTypeID()::MlirTypeID +end + +function mlirTransformAnyParamTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirTransformAnyParamTypeGet(ctx::MlirContext)::MlirType +end + +function mlirTypeIsATransformAnyValueType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsATransformAnyValueType(type::MlirType)::Bool +end + +function mlirTransformAnyValueTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirTransformAnyValueTypeGetTypeID()::MlirTypeID +end + +function mlirTransformAnyValueTypeGet(ctx) + @ccall (MLIR_C_PATH[]).mlirTransformAnyValueTypeGet(ctx::MlirContext)::MlirType +end + +function mlirTypeIsATransformOperationType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsATransformOperationType(type::MlirType)::Bool +end + +function mlirTransformOperationTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirTransformOperationTypeGetTypeID()::MlirTypeID +end + +function mlirTransformOperationTypeGet(ctx, operationName) + @ccall (MLIR_C_PATH[]).mlirTransformOperationTypeGet( + ctx::MlirContext, operationName::MlirStringRef + )::MlirType +end + +function mlirTransformOperationTypeGetOperationName(type) + @ccall (MLIR_C_PATH[]).mlirTransformOperationTypeGetOperationName( + type::MlirType + )::MlirStringRef +end + +function mlirTypeIsATransformParamType(type) + @ccall (MLIR_C_PATH[]).mlirTypeIsATransformParamType(type::MlirType)::Bool +end + +function mlirTransformParamTypeGetTypeID() + @ccall (MLIR_C_PATH[]).mlirTransformParamTypeGetTypeID()::MlirTypeID +end + +function mlirTransformParamTypeGet(ctx, type) + @ccall (MLIR_C_PATH[]).mlirTransformParamTypeGet( + ctx::MlirContext, type::MlirType + )::MlirType +end + +function mlirTransformParamTypeGetType(type) + @ccall (MLIR_C_PATH[]).mlirTransformParamTypeGetType(type::MlirType)::MlirType +end + +struct MlirTransformOptions + ptr::Ptr{Cvoid} +end + +""" + mlirTransformOptionsCreate() + +Creates a default-initialized transform options object. +""" +function mlirTransformOptionsCreate() + @ccall (MLIR_C_PATH[]).mlirTransformOptionsCreate()::MlirTransformOptions +end + +""" + mlirTransformOptionsEnableExpensiveChecks(transformOptions, enable) + +Enables or disables expensive checks in transform options. +""" +function mlirTransformOptionsEnableExpensiveChecks(transformOptions, enable) + @ccall (MLIR_C_PATH[]).mlirTransformOptionsEnableExpensiveChecks( + transformOptions::MlirTransformOptions, enable::Bool + )::Cvoid +end + +""" + mlirTransformOptionsGetExpensiveChecksEnabled(transformOptions) + +Returns true if expensive checks are enabled in transform options. +""" +function mlirTransformOptionsGetExpensiveChecksEnabled(transformOptions) + @ccall (MLIR_C_PATH[]).mlirTransformOptionsGetExpensiveChecksEnabled( + transformOptions::MlirTransformOptions + )::Bool +end + +""" + mlirTransformOptionsEnforceSingleTopLevelTransformOp(transformOptions, enable) + +Enables or disables the enforcement of the top-level transform op being single in transform options. +""" +function mlirTransformOptionsEnforceSingleTopLevelTransformOp(transformOptions, enable) + @ccall (MLIR_C_PATH[]).mlirTransformOptionsEnforceSingleTopLevelTransformOp( + transformOptions::MlirTransformOptions, enable::Bool + )::Cvoid +end + +""" + mlirTransformOptionsGetEnforceSingleTopLevelTransformOp(transformOptions) + +Returns true if the enforcement of the top-level transform op being single is enabled in transform options. +""" +function mlirTransformOptionsGetEnforceSingleTopLevelTransformOp(transformOptions) + @ccall (MLIR_C_PATH[]).mlirTransformOptionsGetEnforceSingleTopLevelTransformOp( + transformOptions::MlirTransformOptions + )::Bool +end + +""" + mlirTransformOptionsDestroy(transformOptions) + +Destroys a transform options object previously created by [`mlirTransformOptionsCreate`](@ref). +""" +function mlirTransformOptionsDestroy(transformOptions) + @ccall (MLIR_C_PATH[]).mlirTransformOptionsDestroy( + transformOptions::MlirTransformOptions + )::Cvoid +end + +""" + mlirTransformApplyNamedSequence(payload, transformRoot, transformModule, transformOptions) + +Applies the transformation script starting at the given transform root operation to the given payload operation. The module containing the transform root as well as the transform options should be provided. The transform operation must implement TransformOpInterface and the module must be a ModuleOp. Returns the status of the application. +""" +function mlirTransformApplyNamedSequence( + payload, transformRoot, transformModule, transformOptions +) + @ccall (MLIR_C_PATH[]).mlirTransformApplyNamedSequence( + payload::MlirOperation, + transformRoot::MlirOperation, + transformModule::MlirOperation, + transformOptions::MlirTransformOptions, + )::MlirLogicalResult +end + +""" + mlirMergeSymbolsIntoFromClone(target, other) + +Merge the symbols from `other` into `target`, potentially renaming them to avoid conflicts. Private symbols may be renamed during the merge, public symbols must have at most one declaration. A name conflict in public symbols is reported as an error before returning a failure. + +Note that this clones the `other` operation unlike the C++ counterpart that takes ownership. +""" +function mlirMergeSymbolsIntoFromClone(target, other) + @ccall (MLIR_C_PATH[]).mlirMergeSymbolsIntoFromClone( + target::MlirOperation, other::MlirOperation + )::MlirLogicalResult +end + +function mlirGetDialectHandle__vector__() + @ccall (MLIR_C_PATH[]).mlirGetDialectHandle__vector__()::MlirDialectHandle +end + +""" + mlirExecutionEngineCreate(op, optLevel, numPaths, sharedLibPaths, enableObjectDump) + +Creates an ExecutionEngine for the provided ModuleOp. The ModuleOp is expected to be "translatable" to LLVM IR (only contains operations in dialects that implement the `LLVMTranslationDialectInterface`). The module ownership stays with the client and can be destroyed as soon as the call returns. `optLevel` is the optimization level to be used for transformation and code generation. LLVM passes at `optLevel` are run before code generation. The number and array of paths corresponding to shared libraries that will be loaded are specified via `numPaths` and `sharedLibPaths` respectively. TODO: figure out other options. +""" +function mlirExecutionEngineCreate(op, optLevel, numPaths, sharedLibPaths, enableObjectDump) + @ccall (MLIR_C_PATH[]).mlirExecutionEngineCreate( + op::MlirModule, + optLevel::Cint, + numPaths::Cint, + sharedLibPaths::Ptr{MlirStringRef}, + enableObjectDump::Bool, + )::MlirExecutionEngine +end + +""" + mlirExecutionEngineDestroy(jit) + +Destroy an ExecutionEngine instance. +""" +function mlirExecutionEngineDestroy(jit) + @ccall (MLIR_C_PATH[]).mlirExecutionEngineDestroy(jit::MlirExecutionEngine)::Cvoid +end + +""" + mlirExecutionEngineIsNull(jit) + +Checks whether an execution engine is null. +""" +function mlirExecutionEngineIsNull(jit) + @ccall (MLIR_C_PATH[]).mlirExecutionEngineIsNull(jit::MlirExecutionEngine)::Bool +end + +""" + mlirExecutionEngineInvokePacked(jit, name, arguments) + +Invoke a native function in the execution engine by name with the arguments and result of the invoked function passed as an array of pointers. The function must have been tagged with the `llvm.emit\\_c\\_interface` attribute. Returns a failure if the execution fails for any reason (the function name can't be resolved for instance). +""" +function mlirExecutionEngineInvokePacked(jit, name, arguments) + @ccall (MLIR_C_PATH[]).mlirExecutionEngineInvokePacked( + jit::MlirExecutionEngine, name::MlirStringRef, arguments::Ptr{Ptr{Cvoid}} + )::MlirLogicalResult +end + +""" + mlirExecutionEngineLookupPacked(jit, name) + +Lookup the wrapper of the native function in the execution engine with the given name, returns nullptr if the function can't be looked-up. +""" +function mlirExecutionEngineLookupPacked(jit, name) + @ccall (MLIR_C_PATH[]).mlirExecutionEngineLookupPacked( + jit::MlirExecutionEngine, name::MlirStringRef + )::Ptr{Cvoid} +end + +""" + mlirExecutionEngineLookup(jit, name) + +Lookup a native function in the execution engine by name, returns nullptr if the name can't be looked-up. +""" +function mlirExecutionEngineLookup(jit, name) + @ccall (MLIR_C_PATH[]).mlirExecutionEngineLookup( + jit::MlirExecutionEngine, name::MlirStringRef + )::Ptr{Cvoid} +end + +""" + mlirExecutionEngineRegisterSymbol(jit, name, sym) + +Register a symbol with the jit: this symbol will be accessible to the jitted code. +""" +function mlirExecutionEngineRegisterSymbol(jit, name, sym) + @ccall (MLIR_C_PATH[]).mlirExecutionEngineRegisterSymbol( + jit::MlirExecutionEngine, name::MlirStringRef, sym::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirExecutionEngineDumpToObjectFile(jit, fileName) + +Dump as an object in `fileName`. +""" +function mlirExecutionEngineDumpToObjectFile(jit, fileName) + @ccall (MLIR_C_PATH[]).mlirExecutionEngineDumpToObjectFile( + jit::MlirExecutionEngine, fileName::MlirStringRef + )::Cvoid +end + +""" + mlirIntegerSetGetContext(set) + +Gets the context in which the given integer set lives. +""" +function mlirIntegerSetGetContext(set) + @ccall (MLIR_C_PATH[]).mlirIntegerSetGetContext(set::MlirIntegerSet)::MlirContext +end + +""" + mlirIntegerSetIsNull(set) + +Checks whether an integer set is a null object. +""" +function mlirIntegerSetIsNull(set) + @ccall (MLIR_C_PATH[]).mlirIntegerSetIsNull(set::MlirIntegerSet)::Bool +end + +""" + mlirIntegerSetEqual(s1, s2) + +Checks if two integer set objects are equal. This is a "shallow" comparison of two objects. Only the sets with some small number of constraints are uniqued and compare equal here. Set objects that represent the same integer set with different constraints may be considered non-equal by this check. Set difference followed by an (expensive) emptiness check should be used to check equivalence of the underlying integer sets. +""" +function mlirIntegerSetEqual(s1, s2) + @ccall (MLIR_C_PATH[]).mlirIntegerSetEqual(s1::MlirIntegerSet, s2::MlirIntegerSet)::Bool +end + +""" + mlirIntegerSetPrint(set, callback, userData) + +Prints an integer set by sending chunks of the string representation and forwarding `userData to `callback`. Note that the callback may be called several times with consecutive chunks of the string. +""" +function mlirIntegerSetPrint(set, callback, userData) + @ccall (MLIR_C_PATH[]).mlirIntegerSetPrint( + set::MlirIntegerSet, callback::MlirStringCallback, userData::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirIntegerSetDump(set) + +Prints an integer set to the standard error stream. +""" +function mlirIntegerSetDump(set) + @ccall (MLIR_C_PATH[]).mlirIntegerSetDump(set::MlirIntegerSet)::Cvoid +end + +""" + mlirIntegerSetEmptyGet(context, numDims, numSymbols) + +Gets or creates a new canonically empty integer set with the give number of dimensions and symbols in the given context. +""" +function mlirIntegerSetEmptyGet(context, numDims, numSymbols) + @ccall (MLIR_C_PATH[]).mlirIntegerSetEmptyGet( + context::MlirContext, numDims::intptr_t, numSymbols::intptr_t + )::MlirIntegerSet +end + +""" + mlirIntegerSetGet(context, numDims, numSymbols, numConstraints, constraints, eqFlags) + +Gets or creates a new integer set in the given context. The set is defined by a list of affine constraints, with the given number of input dimensions and symbols, which are treated as either equalities (eqFlags is 1) or inequalities (eqFlags is 0). Both `constraints` and `eqFlags` are expected to point to at least `numConstraint` consecutive values. +""" +function mlirIntegerSetGet( + context, numDims, numSymbols, numConstraints, constraints, eqFlags +) + @ccall (MLIR_C_PATH[]).mlirIntegerSetGet( + context::MlirContext, + numDims::intptr_t, + numSymbols::intptr_t, + numConstraints::intptr_t, + constraints::Ptr{MlirAffineExpr}, + eqFlags::Ptr{Bool}, + )::MlirIntegerSet +end + +""" + mlirIntegerSetReplaceGet(set, dimReplacements, symbolReplacements, numResultDims, numResultSymbols) + +Gets or creates a new integer set in which the values and dimensions of the given set are replaced with the given affine expressions. `dimReplacements` and `symbolReplacements` are expected to point to at least as many consecutive expressions as the given set has dimensions and symbols, respectively. The new set will have `numResultDims` and `numResultSymbols` dimensions and symbols, respectively. +""" +function mlirIntegerSetReplaceGet( + set, dimReplacements, symbolReplacements, numResultDims, numResultSymbols +) + @ccall (MLIR_C_PATH[]).mlirIntegerSetReplaceGet( + set::MlirIntegerSet, + dimReplacements::Ptr{MlirAffineExpr}, + symbolReplacements::Ptr{MlirAffineExpr}, + numResultDims::intptr_t, + numResultSymbols::intptr_t, + )::MlirIntegerSet +end + +""" + mlirIntegerSetIsCanonicalEmpty(set) + +Checks whether the given set is a canonical empty set, e.g., the set returned by [`mlirIntegerSetEmptyGet`](@ref). +""" +function mlirIntegerSetIsCanonicalEmpty(set) + @ccall (MLIR_C_PATH[]).mlirIntegerSetIsCanonicalEmpty(set::MlirIntegerSet)::Bool +end + +""" + mlirIntegerSetGetNumDims(set) + +Returns the number of dimensions in the given set. +""" +function mlirIntegerSetGetNumDims(set) + @ccall (MLIR_C_PATH[]).mlirIntegerSetGetNumDims(set::MlirIntegerSet)::intptr_t +end + +""" + mlirIntegerSetGetNumSymbols(set) + +Returns the number of symbols in the given set. +""" +function mlirIntegerSetGetNumSymbols(set) + @ccall (MLIR_C_PATH[]).mlirIntegerSetGetNumSymbols(set::MlirIntegerSet)::intptr_t +end + +""" + mlirIntegerSetGetNumInputs(set) + +Returns the number of inputs (dimensions + symbols) in the given set. +""" +function mlirIntegerSetGetNumInputs(set) + @ccall (MLIR_C_PATH[]).mlirIntegerSetGetNumInputs(set::MlirIntegerSet)::intptr_t +end + +""" + mlirIntegerSetGetNumConstraints(set) + +Returns the number of constraints (equalities + inequalities) in the given set. +""" +function mlirIntegerSetGetNumConstraints(set) + @ccall (MLIR_C_PATH[]).mlirIntegerSetGetNumConstraints(set::MlirIntegerSet)::intptr_t +end + +""" + mlirIntegerSetGetNumEqualities(set) + +Returns the number of equalities in the given set. +""" +function mlirIntegerSetGetNumEqualities(set) + @ccall (MLIR_C_PATH[]).mlirIntegerSetGetNumEqualities(set::MlirIntegerSet)::intptr_t +end + +""" + mlirIntegerSetGetNumInequalities(set) + +Returns the number of inequalities in the given set. +""" +function mlirIntegerSetGetNumInequalities(set) + @ccall (MLIR_C_PATH[]).mlirIntegerSetGetNumInequalities(set::MlirIntegerSet)::intptr_t +end + +""" + mlirIntegerSetGetConstraint(set, pos) + +Returns `pos`-th constraint of the set. +""" +function mlirIntegerSetGetConstraint(set, pos) + @ccall (MLIR_C_PATH[]).mlirIntegerSetGetConstraint( + set::MlirIntegerSet, pos::intptr_t + )::MlirAffineExpr +end + +""" + mlirIntegerSetIsConstraintEq(set, pos) + +Returns `true` of the `pos`-th constraint of the set is an equality constraint, `false` otherwise. +""" +function mlirIntegerSetIsConstraintEq(set, pos) + @ccall (MLIR_C_PATH[]).mlirIntegerSetIsConstraintEq( + set::MlirIntegerSet, pos::intptr_t + )::Bool +end + +""" + mlirOperationImplementsInterface(operation, interfaceTypeID) + +Returns `true` if the given operation implements an interface identified by its TypeID. +""" +function mlirOperationImplementsInterface(operation, interfaceTypeID) + @ccall (MLIR_C_PATH[]).mlirOperationImplementsInterface( + operation::MlirOperation, interfaceTypeID::MlirTypeID + )::Bool +end + +""" + mlirOperationImplementsInterfaceStatic(operationName, context, interfaceTypeID) + +Returns `true` if the operation identified by its canonical string name implements the interface identified by its TypeID in the given context. Note that interfaces may be attached to operations in some contexts and not others. +""" +function mlirOperationImplementsInterfaceStatic(operationName, context, interfaceTypeID) + @ccall (MLIR_C_PATH[]).mlirOperationImplementsInterfaceStatic( + operationName::MlirStringRef, context::MlirContext, interfaceTypeID::MlirTypeID + )::Bool +end + +""" + mlirInferTypeOpInterfaceTypeID() + +Returns the interface TypeID of the InferTypeOpInterface. +""" +function mlirInferTypeOpInterfaceTypeID() + @ccall (MLIR_C_PATH[]).mlirInferTypeOpInterfaceTypeID()::MlirTypeID +end + +# typedef void ( * MlirTypesCallback ) ( intptr_t , MlirType * , void * ) +""" +These callbacks are used to return multiple types from functions while transferring ownership to the caller. The first argument is the number of consecutive elements pointed to by the second argument. The third argument is an opaque pointer forwarded to the callback by the caller. +""" +const MlirTypesCallback = Ptr{Cvoid} + +""" + mlirInferTypeOpInterfaceInferReturnTypes(opName, context, location, nOperands, operands, attributes, properties, nRegions, regions, callback, userData) + +Infers the return types of the operation identified by its canonical given the arguments that will be supplied to its generic builder. Calls `callback` with the types of inferred arguments, potentially several times, on success. Returns failure otherwise. +""" +function mlirInferTypeOpInterfaceInferReturnTypes( + opName, + context, + location, + nOperands, + operands, + attributes, + properties, + nRegions, + regions, + callback, + userData, +) + @ccall (MLIR_C_PATH[]).mlirInferTypeOpInterfaceInferReturnTypes( + opName::MlirStringRef, + context::MlirContext, + location::MlirLocation, + nOperands::intptr_t, + operands::Ptr{MlirValue}, + attributes::MlirAttribute, + properties::Ptr{Cvoid}, + nRegions::intptr_t, + regions::Ptr{MlirRegion}, + callback::MlirTypesCallback, + userData::Ptr{Cvoid}, + )::MlirLogicalResult +end + +""" + mlirInferShapedTypeOpInterfaceTypeID() + +Returns the interface TypeID of the InferShapedTypeOpInterface. +""" +function mlirInferShapedTypeOpInterfaceTypeID() + @ccall (MLIR_C_PATH[]).mlirInferShapedTypeOpInterfaceTypeID()::MlirTypeID +end + +# typedef void ( * MlirShapedTypeComponentsCallback ) ( bool , intptr_t , const int64_t * , MlirType , MlirAttribute , void * ) +""" +These callbacks are used to return multiple shaped type components from functions while transferring ownership to the caller. The first argument is the has rank boolean followed by the the rank and a pointer to the shape (if applicable). The next argument is the element type, then the attribute. The last argument is an opaque pointer forwarded to the callback by the caller. This callback will be called potentially multiple times for each shaped type components. +""" +const MlirShapedTypeComponentsCallback = Ptr{Cvoid} + +""" + mlirInferShapedTypeOpInterfaceInferReturnTypes(opName, context, location, nOperands, operands, attributes, properties, nRegions, regions, callback, userData) + +Infers the return shaped type components of the operation. Calls `callback` with the types of inferred arguments on success. Returns failure otherwise. +""" +function mlirInferShapedTypeOpInterfaceInferReturnTypes( + opName, + context, + location, + nOperands, + operands, + attributes, + properties, + nRegions, + regions, + callback, + userData, +) + @ccall (MLIR_C_PATH[]).mlirInferShapedTypeOpInterfaceInferReturnTypes( + opName::MlirStringRef, + context::MlirContext, + location::MlirLocation, + nOperands::intptr_t, + operands::Ptr{MlirValue}, + attributes::MlirAttribute, + properties::Ptr{Cvoid}, + nRegions::intptr_t, + regions::Ptr{MlirRegion}, + callback::MlirShapedTypeComponentsCallback, + userData::Ptr{Cvoid}, + )::MlirLogicalResult +end + +""" + mlirRegisterAllDialects(registry) + +Appends all upstream dialects and extensions to the dialect registry. +""" +function mlirRegisterAllDialects(registry) + @ccall (MLIR_C_PATH[]).mlirRegisterAllDialects(registry::MlirDialectRegistry)::Cvoid +end + +""" + mlirRegisterAllLLVMTranslations(context) + +Register all translations to LLVM IR for dialects that can support it. +""" +function mlirRegisterAllLLVMTranslations(context) + @ccall (MLIR_C_PATH[]).mlirRegisterAllLLVMTranslations(context::MlirContext)::Cvoid +end + +""" + mlirRegisterAllPasses() + +Register all compiler passes of MLIR. +""" +function mlirRegisterAllPasses() + @ccall (MLIR_C_PATH[]).mlirRegisterAllPasses()::Cvoid +end + +struct MlirRewriterBase + ptr::Ptr{Cvoid} +end + +struct MlirFrozenRewritePatternSet + ptr::Ptr{Cvoid} +end + +struct MlirGreedyRewriteDriverConfig + ptr::Ptr{Cvoid} +end + +struct MlirRewritePatternSet + ptr::Ptr{Cvoid} +end + +""" + mlirRewriterBaseGetContext(rewriter) + +Get the MLIR context referenced by the rewriter. +""" +function mlirRewriterBaseGetContext(rewriter) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseGetContext( + rewriter::MlirRewriterBase + )::MlirContext +end + +""" + mlirRewriterBaseClearInsertionPoint(rewriter) + +Reset the insertion point to no location. Creating an operation without a set insertion point is an error, but this can still be useful when the current insertion point a builder refers to is being removed. +""" +function mlirRewriterBaseClearInsertionPoint(rewriter) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseClearInsertionPoint( + rewriter::MlirRewriterBase + )::Cvoid +end + +""" + mlirRewriterBaseSetInsertionPointBefore(rewriter, op) + +Sets the insertion point to the specified operation, which will cause subsequent insertions to go right before it. +""" +function mlirRewriterBaseSetInsertionPointBefore(rewriter, op) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseSetInsertionPointBefore( + rewriter::MlirRewriterBase, op::MlirOperation + )::Cvoid +end + +""" + mlirRewriterBaseSetInsertionPointAfter(rewriter, op) + +Sets the insertion point to the node after the specified operation, which will cause subsequent insertions to go right after it. +""" +function mlirRewriterBaseSetInsertionPointAfter(rewriter, op) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseSetInsertionPointAfter( + rewriter::MlirRewriterBase, op::MlirOperation + )::Cvoid +end + +""" + mlirRewriterBaseSetInsertionPointAfterValue(rewriter, value) + +Sets the insertion point to the node after the specified value. If value has a defining operation, sets the insertion point to the node after such defining operation. This will cause subsequent insertions to go right after it. Otherwise, value is a BlockArgument. Sets the insertion point to the start of its block. +""" +function mlirRewriterBaseSetInsertionPointAfterValue(rewriter, value) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseSetInsertionPointAfterValue( + rewriter::MlirRewriterBase, value::MlirValue + )::Cvoid +end + +""" + mlirRewriterBaseSetInsertionPointToStart(rewriter, block) + +Sets the insertion point to the start of the specified block. +""" +function mlirRewriterBaseSetInsertionPointToStart(rewriter, block) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseSetInsertionPointToStart( + rewriter::MlirRewriterBase, block::MlirBlock + )::Cvoid +end + +""" + mlirRewriterBaseSetInsertionPointToEnd(rewriter, block) + +Sets the insertion point to the end of the specified block. +""" +function mlirRewriterBaseSetInsertionPointToEnd(rewriter, block) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseSetInsertionPointToEnd( + rewriter::MlirRewriterBase, block::MlirBlock + )::Cvoid +end + +""" + mlirRewriterBaseGetInsertionBlock(rewriter) + +Return the block the current insertion point belongs to. Note that the insertion point is not necessarily the end of the block. +""" +function mlirRewriterBaseGetInsertionBlock(rewriter) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseGetInsertionBlock( + rewriter::MlirRewriterBase + )::MlirBlock +end + +""" + mlirRewriterBaseGetBlock(rewriter) + +Returns the current block of the rewriter. +""" +function mlirRewriterBaseGetBlock(rewriter) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseGetBlock(rewriter::MlirRewriterBase)::MlirBlock +end + +""" + mlirRewriterBaseCreateBlockBefore(rewriter, insertBefore, nArgTypes, argTypes, locations) + +Add new block with 'argTypes' arguments and set the insertion point to the end of it. The block is placed before 'insertBefore'. `locs` contains the locations of the inserted arguments, and should match the size of `argTypes`. +""" +function mlirRewriterBaseCreateBlockBefore( + rewriter, insertBefore, nArgTypes, argTypes, locations +) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseCreateBlockBefore( + rewriter::MlirRewriterBase, + insertBefore::MlirBlock, + nArgTypes::intptr_t, + argTypes::Ptr{MlirType}, + locations::Ptr{MlirLocation}, + )::MlirBlock +end + +""" + mlirRewriterBaseInsert(rewriter, op) + +Insert the given operation at the current insertion point and return it. +""" +function mlirRewriterBaseInsert(rewriter, op) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseInsert( + rewriter::MlirRewriterBase, op::MlirOperation + )::MlirOperation +end + +""" + mlirRewriterBaseClone(rewriter, op) + +Creates a deep copy of the specified operation. +""" +function mlirRewriterBaseClone(rewriter, op) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseClone( + rewriter::MlirRewriterBase, op::MlirOperation + )::MlirOperation +end + +""" + mlirRewriterBaseCloneWithoutRegions(rewriter, op) + +Creates a deep copy of this operation but keep the operation regions empty. +""" +function mlirRewriterBaseCloneWithoutRegions(rewriter, op) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseCloneWithoutRegions( + rewriter::MlirRewriterBase, op::MlirOperation + )::MlirOperation +end + +""" + mlirRewriterBaseCloneRegionBefore(rewriter, region, before) + +Clone the blocks that belong to "region" before the given position in another region "parent". +""" +function mlirRewriterBaseCloneRegionBefore(rewriter, region, before) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseCloneRegionBefore( + rewriter::MlirRewriterBase, region::MlirRegion, before::MlirBlock + )::Cvoid +end + +""" + mlirRewriterBaseInlineRegionBefore(rewriter, region, before) + +Move the blocks that belong to "region" before the given position in another region "parent". The two regions must be different. The caller is responsible for creating or updating the operation transferring flow of control to the region and passing it the correct block arguments. +""" +function mlirRewriterBaseInlineRegionBefore(rewriter, region, before) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseInlineRegionBefore( + rewriter::MlirRewriterBase, region::MlirRegion, before::MlirBlock + )::Cvoid +end + +""" + mlirRewriterBaseReplaceOpWithValues(rewriter, op, nValues, values) + +Replace the results of the given (original) operation with the specified list of values (replacements). The result types of the given op and the replacements must match. The original op is erased. +""" +function mlirRewriterBaseReplaceOpWithValues(rewriter, op, nValues, values) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseReplaceOpWithValues( + rewriter::MlirRewriterBase, + op::MlirOperation, + nValues::intptr_t, + values::Ptr{MlirValue}, + )::Cvoid +end + +""" + mlirRewriterBaseReplaceOpWithOperation(rewriter, op, newOp) + +Replace the results of the given (original) operation with the specified new op (replacement). The result types of the two ops must match. The original op is erased. +""" +function mlirRewriterBaseReplaceOpWithOperation(rewriter, op, newOp) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseReplaceOpWithOperation( + rewriter::MlirRewriterBase, op::MlirOperation, newOp::MlirOperation + )::Cvoid +end + +""" + mlirRewriterBaseEraseOp(rewriter, op) + +Erases an operation that is known to have no uses. +""" +function mlirRewriterBaseEraseOp(rewriter, op) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseEraseOp( + rewriter::MlirRewriterBase, op::MlirOperation + )::Cvoid +end + +""" + mlirRewriterBaseEraseBlock(rewriter, block) + +Erases a block along with all operations inside it. +""" +function mlirRewriterBaseEraseBlock(rewriter, block) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseEraseBlock( + rewriter::MlirRewriterBase, block::MlirBlock + )::Cvoid +end + +""" + mlirRewriterBaseInlineBlockBefore(rewriter, source, op, nArgValues, argValues) + +Inline the operations of block 'source' before the operation 'op'. The source block will be deleted and must have no uses. 'argValues' is used to replace the block arguments of 'source' + +The source block must have no successors. Otherwise, the resulting IR would have unreachable operations. +""" +function mlirRewriterBaseInlineBlockBefore(rewriter, source, op, nArgValues, argValues) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseInlineBlockBefore( + rewriter::MlirRewriterBase, + source::MlirBlock, + op::MlirOperation, + nArgValues::intptr_t, + argValues::Ptr{MlirValue}, + )::Cvoid +end + +""" + mlirRewriterBaseMergeBlocks(rewriter, source, dest, nArgValues, argValues) + +Inline the operations of block 'source' into the end of block 'dest'. The source block will be deleted and must have no uses. 'argValues' is used to replace the block arguments of 'source' + +The dest block must have no successors. Otherwise, the resulting IR would have unreachable operation. +""" +function mlirRewriterBaseMergeBlocks(rewriter, source, dest, nArgValues, argValues) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseMergeBlocks( + rewriter::MlirRewriterBase, + source::MlirBlock, + dest::MlirBlock, + nArgValues::intptr_t, + argValues::Ptr{MlirValue}, + )::Cvoid +end + +""" + mlirRewriterBaseMoveOpBefore(rewriter, op, existingOp) + +Unlink this operation from its current block and insert it right before `existingOp` which may be in the same or another block in the same function. +""" +function mlirRewriterBaseMoveOpBefore(rewriter, op, existingOp) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseMoveOpBefore( + rewriter::MlirRewriterBase, op::MlirOperation, existingOp::MlirOperation + )::Cvoid +end + +""" + mlirRewriterBaseMoveOpAfter(rewriter, op, existingOp) + +Unlink this operation from its current block and insert it right after `existingOp` which may be in the same or another block in the same function. +""" +function mlirRewriterBaseMoveOpAfter(rewriter, op, existingOp) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseMoveOpAfter( + rewriter::MlirRewriterBase, op::MlirOperation, existingOp::MlirOperation + )::Cvoid +end + +""" + mlirRewriterBaseMoveBlockBefore(rewriter, block, existingBlock) + +Unlink this block and insert it right before `existingBlock`. +""" +function mlirRewriterBaseMoveBlockBefore(rewriter, block, existingBlock) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseMoveBlockBefore( + rewriter::MlirRewriterBase, block::MlirBlock, existingBlock::MlirBlock + )::Cvoid +end + +""" + mlirRewriterBaseStartOpModification(rewriter, op) + +This method is used to notify the rewriter that an in-place operation modification is about to happen. A call to this function *must* be followed by a call to either `finalizeOpModification` or `cancelOpModification`. This is a minor efficiency win (it avoids creating a new operation and removing the old one) but also often allows simpler code in the client. +""" +function mlirRewriterBaseStartOpModification(rewriter, op) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseStartOpModification( + rewriter::MlirRewriterBase, op::MlirOperation + )::Cvoid +end + +""" + mlirRewriterBaseFinalizeOpModification(rewriter, op) + +This method is used to signal the end of an in-place modification of the given operation. This can only be called on operations that were provided to a call to `startOpModification`. +""" +function mlirRewriterBaseFinalizeOpModification(rewriter, op) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseFinalizeOpModification( + rewriter::MlirRewriterBase, op::MlirOperation + )::Cvoid +end + +""" + mlirRewriterBaseCancelOpModification(rewriter, op) + +This method cancels a pending in-place modification. This can only be called on operations that were provided to a call to `startOpModification`. +""" +function mlirRewriterBaseCancelOpModification(rewriter, op) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseCancelOpModification( + rewriter::MlirRewriterBase, op::MlirOperation + )::Cvoid +end + +""" + mlirRewriterBaseReplaceAllUsesWith(rewriter, from, to) + +Find uses of `from` and replace them with `to`. Also notify the listener about every in-place op modification (for every use that was replaced). +""" +function mlirRewriterBaseReplaceAllUsesWith(rewriter, from, to) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseReplaceAllUsesWith( + rewriter::MlirRewriterBase, from::MlirValue, to::MlirValue + )::Cvoid +end + +""" + mlirRewriterBaseReplaceAllValueRangeUsesWith(rewriter, nValues, from, to) + +Find uses of `from` and replace them with `to`. Also notify the listener about every in-place op modification (for every use that was replaced). +""" +function mlirRewriterBaseReplaceAllValueRangeUsesWith(rewriter, nValues, from, to) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseReplaceAllValueRangeUsesWith( + rewriter::MlirRewriterBase, + nValues::intptr_t, + from::Ptr{MlirValue}, + to::Ptr{MlirValue}, + )::Cvoid +end + +""" + mlirRewriterBaseReplaceAllOpUsesWithValueRange(rewriter, from, nTo, to) + +Find uses of `from` and replace them with `to`. Also notify the listener about every in-place op modification (for every use that was replaced) and that the `from` operation is about to be replaced. +""" +function mlirRewriterBaseReplaceAllOpUsesWithValueRange(rewriter, from, nTo, to) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseReplaceAllOpUsesWithValueRange( + rewriter::MlirRewriterBase, from::MlirOperation, nTo::intptr_t, to::Ptr{MlirValue} + )::Cvoid +end + +""" + mlirRewriterBaseReplaceAllOpUsesWithOperation(rewriter, from, to) + +Find uses of `from` and replace them with `to`. Also notify the listener about every in-place op modification (for every use that was replaced) and that the `from` operation is about to be replaced. +""" +function mlirRewriterBaseReplaceAllOpUsesWithOperation(rewriter, from, to) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseReplaceAllOpUsesWithOperation( + rewriter::MlirRewriterBase, from::MlirOperation, to::MlirOperation + )::Cvoid +end + +""" + mlirRewriterBaseReplaceOpUsesWithinBlock(rewriter, op, nNewValues, newValues, block) + +Find uses of `from` within `block` and replace them with `to`. Also notify the listener about every in-place op modification (for every use that was replaced). The optional `allUsesReplaced` flag is set to "true" if all uses were replaced. +""" +function mlirRewriterBaseReplaceOpUsesWithinBlock( + rewriter, op, nNewValues, newValues, block +) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseReplaceOpUsesWithinBlock( + rewriter::MlirRewriterBase, + op::MlirOperation, + nNewValues::intptr_t, + newValues::Ptr{MlirValue}, + block::MlirBlock, + )::Cvoid +end + +""" + mlirRewriterBaseReplaceAllUsesExcept(rewriter, from, to, exceptedUser) + +Find uses of `from` and replace them with `to` except if the user is `exceptedUser`. Also notify the listener about every in-place op modification (for every use that was replaced). +""" +function mlirRewriterBaseReplaceAllUsesExcept(rewriter, from, to, exceptedUser) + @ccall (MLIR_C_PATH[]).mlirRewriterBaseReplaceAllUsesExcept( + rewriter::MlirRewriterBase, + from::MlirValue, + to::MlirValue, + exceptedUser::MlirOperation, + )::Cvoid +end + +""" + mlirIRRewriterCreate(context) + +Create an IRRewriter and transfer ownership to the caller. +""" +function mlirIRRewriterCreate(context) + @ccall (MLIR_C_PATH[]).mlirIRRewriterCreate(context::MlirContext)::MlirRewriterBase +end + +""" + mlirIRRewriterCreateFromOp(op) + +Create an IRRewriter and transfer ownership to the caller. Additionally set the insertion point before the operation. +""" +function mlirIRRewriterCreateFromOp(op) + @ccall (MLIR_C_PATH[]).mlirIRRewriterCreateFromOp(op::MlirOperation)::MlirRewriterBase +end + +""" + mlirIRRewriterDestroy(rewriter) + +Takes an IRRewriter owned by the caller and destroys it. It is the responsibility of the user to only pass an IRRewriter class. +""" +function mlirIRRewriterDestroy(rewriter) + @ccall (MLIR_C_PATH[]).mlirIRRewriterDestroy(rewriter::MlirRewriterBase)::Cvoid +end + +""" + mlirFreezeRewritePattern(op) + +FrozenRewritePatternSet API +""" +function mlirFreezeRewritePattern(op) + @ccall (MLIR_C_PATH[]).mlirFreezeRewritePattern( + op::MlirRewritePatternSet + )::MlirFrozenRewritePatternSet +end + +function mlirFrozenRewritePatternSetDestroy(op) + @ccall (MLIR_C_PATH[]).mlirFrozenRewritePatternSetDestroy( + op::MlirFrozenRewritePatternSet + )::Cvoid +end + +function mlirApplyPatternsAndFoldGreedily(op, patterns, arg3) + @ccall (MLIR_C_PATH[]).mlirApplyPatternsAndFoldGreedily( + op::MlirModule, + patterns::MlirFrozenRewritePatternSet, + arg3::MlirGreedyRewriteDriverConfig, + )::MlirLogicalResult +end + +struct MlirPDLPatternModule + ptr::Ptr{Cvoid} +end + +function mlirPDLPatternModuleFromModule(op) + @ccall (MLIR_C_PATH[]).mlirPDLPatternModuleFromModule( + op::MlirModule + )::MlirPDLPatternModule +end + +function mlirPDLPatternModuleDestroy(op) + @ccall (MLIR_C_PATH[]).mlirPDLPatternModuleDestroy(op::MlirPDLPatternModule)::Cvoid +end + +function mlirRewritePatternSetFromPDLPatternModule(op) + @ccall (MLIR_C_PATH[]).mlirRewritePatternSetFromPDLPatternModule( + op::MlirPDLPatternModule + )::MlirRewritePatternSet +end + +""" +` LLVMCSupportTypes Types and Enumerations` + +@{ +""" +const LLVMBool = Cint + +mutable struct LLVMOpaqueMemoryBuffer end + +""" +Used to pass regions of memory through LLVM interfaces. + +# See also +llvm::MemoryBuffer +""" +const LLVMMemoryBufferRef = Ptr{LLVMOpaqueMemoryBuffer} + +mutable struct LLVMOpaqueContext end + +""" +The top-level container for all LLVM global data. See the LLVMContext class. +""" +const LLVMContextRef = Ptr{LLVMOpaqueContext} + +mutable struct LLVMOpaqueModule end + +""" +The top-level container for all other LLVM Intermediate Representation (IR) objects. + +# See also +llvm::Module +""" +const LLVMModuleRef = Ptr{LLVMOpaqueModule} + +mutable struct LLVMOpaqueType end + +""" +Each value in the LLVM IR has a type, an [`LLVMTypeRef`](@ref). + +# See also +llvm::Type +""" +const LLVMTypeRef = Ptr{LLVMOpaqueType} + +mutable struct LLVMOpaqueValue end + +""" +Represents an individual value in LLVM IR. + +This models llvm::Value. +""" +const LLVMValueRef = Ptr{LLVMOpaqueValue} + +mutable struct LLVMOpaqueBasicBlock end + +""" +Represents a basic block of instructions in LLVM IR. + +This models llvm::BasicBlock. +""" +const LLVMBasicBlockRef = Ptr{LLVMOpaqueBasicBlock} + +mutable struct LLVMOpaqueMetadata end + +""" +Represents an LLVM Metadata. + +This models llvm::Metadata. +""" +const LLVMMetadataRef = Ptr{LLVMOpaqueMetadata} + +mutable struct LLVMOpaqueNamedMDNode end + +""" +Represents an LLVM Named Metadata Node. + +This models llvm::NamedMDNode. +""" +const LLVMNamedMDNodeRef = Ptr{LLVMOpaqueNamedMDNode} + +mutable struct LLVMOpaqueValueMetadataEntry end + +""" +Represents an entry in a Global Object's metadata attachments. + +This models std::pair +""" +const LLVMValueMetadataEntry = LLVMOpaqueValueMetadataEntry + +mutable struct LLVMOpaqueBuilder end + +""" +Represents an LLVM basic block builder. + +This models llvm::IRBuilder. +""" +const LLVMBuilderRef = Ptr{LLVMOpaqueBuilder} + +mutable struct LLVMOpaqueDIBuilder end + +""" +Represents an LLVM debug info builder. + +This models llvm::DIBuilder. +""" +const LLVMDIBuilderRef = Ptr{LLVMOpaqueDIBuilder} + +mutable struct LLVMOpaqueModuleProvider end + +""" +Interface used to provide a module to JIT or interpreter. This is now just a synonym for llvm::Module, but we have to keep using the different type to keep binary compatibility. +""" +const LLVMModuleProviderRef = Ptr{LLVMOpaqueModuleProvider} + +mutable struct LLVMOpaquePassManager end + +""" +# See also +llvm::PassManagerBase +""" +const LLVMPassManagerRef = Ptr{LLVMOpaquePassManager} + +mutable struct LLVMOpaqueUse end + +""" +Used to get the users and usees of a Value. + +# See also +llvm::Use +""" +const LLVMUseRef = Ptr{LLVMOpaqueUse} + +mutable struct LLVMOpaqueOperandBundle end + +""" +# See also +llvm::OperandBundleDef +""" +const LLVMOperandBundleRef = Ptr{LLVMOpaqueOperandBundle} + +mutable struct LLVMOpaqueAttributeRef end + +""" +Used to represent an attributes. + +# See also +llvm::Attribute +""" +const LLVMAttributeRef = Ptr{LLVMOpaqueAttributeRef} + +mutable struct LLVMOpaqueDiagnosticInfo end + +""" +# See also +llvm::DiagnosticInfo +""" +const LLVMDiagnosticInfoRef = Ptr{LLVMOpaqueDiagnosticInfo} + +mutable struct LLVMComdat end + +""" +# See also +llvm::Comdat +""" +const LLVMComdatRef = Ptr{LLVMComdat} + +mutable struct LLVMOpaqueModuleFlagEntry end + +""" +# See also +llvm::Module::ModuleFlagEntry +""" +const LLVMModuleFlagEntry = LLVMOpaqueModuleFlagEntry + +mutable struct LLVMOpaqueJITEventListener end + +""" +# See also +llvm::JITEventListener +""" +const LLVMJITEventListenerRef = Ptr{LLVMOpaqueJITEventListener} + +mutable struct LLVMOpaqueBinary end + +""" +# See also +llvm::object::Binary +""" +const LLVMBinaryRef = Ptr{LLVMOpaqueBinary} + +mutable struct LLVMOpaqueDbgRecord end + +""" +# See also +llvm::DbgRecord +""" +const LLVMDbgRecordRef = Ptr{LLVMOpaqueDbgRecord} + +""" + LLVMLoadLibraryPermanently(Filename) + +This function permanently loads the dynamic library at the given path. It is safe to call this function multiple times for the same library. + +# See also +sys::DynamicLibrary::LoadLibraryPermanently() +""" +function LLVMLoadLibraryPermanently(Filename) + @ccall (MLIR_C_PATH[]).LLVMLoadLibraryPermanently(Filename::Cstring)::LLVMBool +end + +""" + LLVMParseCommandLineOptions(argc, argv, Overview) + +This function parses the given arguments using the LLVM command line parser. Note that the only stable thing about this function is its signature; you cannot rely on any particular set of command line arguments being interpreted the same way across LLVM versions. + +# See also +llvm::cl::ParseCommandLineOptions() +""" +function LLVMParseCommandLineOptions(argc, argv, Overview) + @ccall (MLIR_C_PATH[]).LLVMParseCommandLineOptions( + argc::Cint, argv::Ptr{Cstring}, Overview::Cstring + )::Cvoid +end + +""" + LLVMSearchForAddressOfSymbol(symbolName) + +This function will search through all previously loaded dynamic libraries for the symbol `symbolName`. If it is found, the address of that symbol is returned. If not, null is returned. + +# See also +sys::DynamicLibrary::SearchForAddressOfSymbol() +""" +function LLVMSearchForAddressOfSymbol(symbolName) + @ccall (MLIR_C_PATH[]).LLVMSearchForAddressOfSymbol(symbolName::Cstring)::Ptr{Cvoid} +end + +""" + LLVMAddSymbol(symbolName, symbolValue) + +This functions permanently adds the symbol `symbolName` with the value `symbolValue`. These symbols are searched before any libraries. + +# See also +sys::DynamicLibrary::AddSymbol() +""" +function LLVMAddSymbol(symbolName, symbolValue) + @ccall (MLIR_C_PATH[]).LLVMAddSymbol( + symbolName::Cstring, symbolValue::Ptr{Cvoid} + )::Cvoid +end + +""" + mlirTranslateModuleToLLVMIR(_module, context) + +Translate operation that satisfies LLVM dialect module requirements into an LLVM IR module living in the given context. This translates operations from any dilalect that has a registered implementation of LLVMTranslationDialectInterface. + +# Returns +the generated LLVM IR Module from the translated MLIR module, it is owned by the caller. +""" +function mlirTranslateModuleToLLVMIR(_module, context) + @ccall (MLIR_C_PATH[]).mlirTranslateModuleToLLVMIR( + _module::MlirOperation, context::LLVMContextRef + )::LLVMModuleRef +end + +function mlirRegisterTransformsPasses() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsPasses()::Cvoid +end + +function mlirCreateTransformsCSE() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsCSE()::MlirPass +end + +function mlirRegisterTransformsCSE() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsCSE()::Cvoid +end + +function mlirCreateTransformsCanonicalizer() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsCanonicalizer()::MlirPass +end + +function mlirRegisterTransformsCanonicalizer() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsCanonicalizer()::Cvoid +end + +function mlirCreateTransformsCompositeFixedPointPass() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsCompositeFixedPointPass()::MlirPass +end + +function mlirRegisterTransformsCompositeFixedPointPass() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsCompositeFixedPointPass()::Cvoid +end + +function mlirCreateTransformsControlFlowSink() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsControlFlowSink()::MlirPass +end + +function mlirRegisterTransformsControlFlowSink() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsControlFlowSink()::Cvoid +end + +function mlirCreateTransformsGenerateRuntimeVerification() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsGenerateRuntimeVerification()::MlirPass +end + +function mlirRegisterTransformsGenerateRuntimeVerification() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsGenerateRuntimeVerification()::Cvoid +end + +function mlirCreateTransformsInliner() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsInliner()::MlirPass +end + +function mlirRegisterTransformsInliner() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsInliner()::Cvoid +end + +function mlirCreateTransformsLocationSnapshot() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsLocationSnapshot()::MlirPass +end + +function mlirRegisterTransformsLocationSnapshot() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsLocationSnapshot()::Cvoid +end + +function mlirCreateTransformsLoopInvariantCodeMotion() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsLoopInvariantCodeMotion()::MlirPass +end + +function mlirRegisterTransformsLoopInvariantCodeMotion() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsLoopInvariantCodeMotion()::Cvoid +end + +function mlirCreateTransformsLoopInvariantSubsetHoisting() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsLoopInvariantSubsetHoisting()::MlirPass +end + +function mlirRegisterTransformsLoopInvariantSubsetHoisting() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsLoopInvariantSubsetHoisting()::Cvoid +end + +function mlirCreateTransformsMem2Reg() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsMem2Reg()::MlirPass +end + +function mlirRegisterTransformsMem2Reg() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsMem2Reg()::Cvoid +end + +function mlirCreateTransformsPrintIRPass() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsPrintIRPass()::MlirPass +end + +function mlirRegisterTransformsPrintIRPass() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsPrintIRPass()::Cvoid +end + +function mlirCreateTransformsPrintOpStats() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsPrintOpStats()::MlirPass +end + +function mlirRegisterTransformsPrintOpStats() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsPrintOpStats()::Cvoid +end + +function mlirCreateTransformsRemoveDeadValues() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsRemoveDeadValues()::MlirPass +end + +function mlirRegisterTransformsRemoveDeadValues() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsRemoveDeadValues()::Cvoid +end + +function mlirCreateTransformsSCCP() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsSCCP()::MlirPass +end + +function mlirRegisterTransformsSCCP() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsSCCP()::Cvoid +end + +function mlirCreateTransformsSROA() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsSROA()::MlirPass +end + +function mlirRegisterTransformsSROA() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsSROA()::Cvoid +end + +function mlirCreateTransformsStripDebugInfo() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsStripDebugInfo()::MlirPass +end + +function mlirRegisterTransformsStripDebugInfo() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsStripDebugInfo()::Cvoid +end + +function mlirCreateTransformsSymbolDCE() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsSymbolDCE()::MlirPass +end + +function mlirRegisterTransformsSymbolDCE() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsSymbolDCE()::Cvoid +end + +function mlirCreateTransformsSymbolPrivatize() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsSymbolPrivatize()::MlirPass +end + +function mlirRegisterTransformsSymbolPrivatize() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsSymbolPrivatize()::Cvoid +end + +function mlirCreateTransformsTopologicalSort() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsTopologicalSort()::MlirPass +end + +function mlirRegisterTransformsTopologicalSort() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsTopologicalSort()::Cvoid +end + +function mlirCreateTransformsViewOpGraph() + @ccall (MLIR_C_PATH[]).mlirCreateTransformsViewOpGraph()::MlirPass +end + +function mlirRegisterTransformsViewOpGraph() + @ccall (MLIR_C_PATH[]).mlirRegisterTransformsViewOpGraph()::Cvoid +end + +const MLIR_CAPI_DWARF_ADDRESS_SPACE_NULL = -1 + +const MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS = 0 + +const MLIR_ENABLE_PDL_IN_PATTERNMATCH = 1 + +const MLIR_ENABLE_NVPTXCOMPILER = 0 + +const MLIR_ENABLE_ROCM_CONVERSIONS = 0 diff --git a/src/API/API.jl b/src/API/API.jl index ab3239f0..f927a7b3 100644 --- a/src/API/API.jl +++ b/src/API/API.jl @@ -1,6 +1,6 @@ module API -using ..MLIR: MLIR_VERSION, MLIRException +using ..MLIR: MLIR_VERSION, MLIRException, MLIR_VERSION_MIN, MLIR_VERSION_MAX include("Types.jl") using .Types @@ -18,14 +18,14 @@ end # generate version-less API functions begin - local ops = mapreduce(∪, [v14, v15, v16, v17, v18]) do mod + local ops = mapreduce(∪, [v14, v15, v16, v17, v18, v19]) do mod filter(names(mod; all=true)) do name name ∉ [nameof(mod), :eval, :include] && !startswith(string(name), '#') end end for op in ops - container_mods = filter([v14, v15, v16, v17, v18]) do mod + container_mods = filter([v14, v15, v16, v17, v18, v19]) do mod op in names(mod; all=true) end container_mods = map(container_mods) do mod @@ -34,7 +34,7 @@ begin @eval function $op(args...; kwargs...) version = MLIR_VERSION[] - if v"14" > version <= v"17" + if !($MLIR_VERSION_MIN <= version <= $MLIR_VERSION_MAX) error("Unsupported MLIR version $version") end diff --git a/src/Dialects/19/AMDGPU.jl b/src/Dialects/19/AMDGPU.jl new file mode 100644 index 00000000..0d77f294 --- /dev/null +++ b/src/Dialects/19/AMDGPU.jl @@ -0,0 +1,696 @@ +module amdgpu + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`ext_packed_fp8` + +Extend the value `source[index]` to a 32-bit float and return it. + +This rather unusual signature arises from the fact that AMD GPUs cannot +easily work with sub 32-bit quantities, so the compiler intrinsics for +extending 8-bit floats (which are, currently, the only way to work with +this operation) take packed vectors of 4 such floats. + +If the passed-in vector has fewer than four elements, or the input is scalar, +the remaining values in the <4 x i8> will be filled with with +undefined values as needed. +""" +function ext_packed_fp8(source::Value; res::IR.Type, index, location=Location()) + _results = IR.Type[res,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("index", index),] + + return IR.create_operation( + "amdgpu.ext_packed_fp8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`lds_barrier` + +`amdgpu.lds_barrier` is both a barrier (all workitems in a workgroup must reach +the barrier before any of them may proceed past it) and a wait for all +operations that affect the Local Data Store (LDS) issued from that wrokgroup +to complete before the workgroup may continue. Since the LDS is per-workgroup +memory, this barrier may be used, for example, to ensure all workitems have +written data to LDS before any workitem attempts to read from it. + +Note that `lds_barrier` does **not** force reads to or from global memory +to complete before execution continues. Therefore, it should be used when +operations on global memory can be issued far in advance of when their results +are used (for example, by writing them to LDS). + +WARNING: On architectures that do not support the BackOffBarrier feature, +(those which will implement this barrier by emitting inline assembly), +use of this operation will impede the usabiliity of memory watches (including +breakpoints set on variables) when debugging. +""" +function lds_barrier(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amdgpu.lds_barrier", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma` + +The `amdgpu.mfma` op is an MLIR wrapper around intrinsics +for various `mfma` instructions in the CDNA architecture, which perform +multiple outer products in order to allow fast matrix multiplication. + +The wrapper will select an appropriate `mfma` instruction, if one is available, +based on the provided `m`, `k`, `n`, and `nBlks` attributes, along with the +types of the source and destination arguments. + +For information on the layouts of the input and output matrces (which are stored +in `sourceA`, `sourceB`, `destC`, and `destD`), see the CDNA ISA documentation. + +The `cbsz`, `abid`, and `blgp` parameters control how the lanes of the wave +are permuted when matrix data is being loaded: `blgp` can be any number of +fixed permutations, `cbsz` specifies the log_2 of the number of chunks the lanes +holding sourceA are split into, and `abid` selects one of those chunks. + +Note, this wrapper allows specifying `vector<4Kxi8>` arguments to MFMA +intrinsics that take an integer type of width `4K`. For example, +one can provide a vector<4xi8> as an argument to an MFMA instruction that +logically takes 4 i8s but whose intrinsics are specified to take an i32. +In these cases, the bytes in the vector will be concatenated in little-endian +order (that is, v[0] will go to arg[7:0], v[1] to arg[15:8] and so on). + +The negateA, negateB, and negateC flags are only supported for double-precision +operations on gfx940+. +""" +function mfma( + sourceA::Value, + sourceB::Value, + destC::Value; + destD::IR.Type, + m, + n, + k, + blocks, + cbsz=nothing, + abid=nothing, + blgp=nothing, + reducePrecision=nothing, + negateA=nothing, + negateB=nothing, + negateC=nothing, + location=Location(), +) + _results = IR.Type[destD,] + _operands = Value[sourceA, sourceB, destC] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("m", m), + namedattribute("n", n), + namedattribute("k", k), + namedattribute("blocks", blocks), + ] + !isnothing(cbsz) && push!(_attributes, namedattribute("cbsz", cbsz)) + !isnothing(abid) && push!(_attributes, namedattribute("abid", abid)) + !isnothing(blgp) && push!(_attributes, namedattribute("blgp", blgp)) + !isnothing(reducePrecision) && + push!(_attributes, namedattribute("reducePrecision", reducePrecision)) + !isnothing(negateA) && push!(_attributes, namedattribute("negateA", negateA)) + !isnothing(negateB) && push!(_attributes, namedattribute("negateB", negateB)) + !isnothing(negateC) && push!(_attributes, namedattribute("negateC", negateC)) + + return IR.create_operation( + "amdgpu.mfma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`packed_stoch_round_fp8` + +Round the input `source`, adding in `stochiasticParam`, and place it into +the `storeIndex`th element of `res`. + +If `existing` is passed in, elements of `res` other than the one at `storeIndex` +are copied from `existing`. + +The reason for this odd signature is that AMD GPUs cannot easily work with +sub-registers, and so the conversion intrinsics (which are currently the +only way to work with 8-bit float types) take packed vectors of 4 8-bit +values. +""" +function packed_stoch_round_fp8( + source::Value, + stochiasticParam::Value, + existing=nothing::Union{Nothing,Value}; + res::IR.Type, + storeIndex, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[source, stochiasticParam] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("storeIndex", storeIndex),] + !isnothing(existing) && push!(_operands, existing) + + return IR.create_operation( + "amdgpu.packed_stoch_round_fp8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`packed_trunc_2xfp8` + +Round the inputs `sourceA` and `sourceB` (which is undefined if not +specified) into the low or high word (bottom two or top two) elements +of the returned vector, keeping the other two elements of `existing` +unchanged if present (or undefined if it was not passed in). + +The reason for this odd signature is that AMD GPUs cannot easily work with +sub-registers, and so the conversion intrinsics (which are currently the +only way to work with 8-bit float types) take packed vectors of 4 8-bit +values. +""" +function packed_trunc_2xfp8( + sourceA::Value, + sourceB=nothing::Union{Nothing,Value}; + existing=nothing::Union{Nothing,Value}, + res::IR.Type, + wordIndex, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[sourceA,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("wordIndex", wordIndex),] + !isnothing(sourceB) && push!(_operands, sourceB) + !isnothing(existing) && push!(_operands, existing) + push!( + _attributes, + operandsegmentsizes([1, isnothing(sourceB) ? 0 : 1, isnothing(existing) ? 0 : 1]), + ) + + return IR.create_operation( + "amdgpu.packed_trunc_2xfp8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_atomic_cmpswap` + +The `amdgpu.raw_buffer_atomic_cmpswap` op is a wrapper around the +buffer-based atomic compare-and-swap min available on AMD GPUs. + +The index into the buffer is computed as for `memref.store` with the addition +of `indexOffset` (which is used to aid in emitting vectorized code) and, +if present `sgprOffset` (which is added after bounds checks and includes +any non-zero offset on the memref type). + +All indexing components are given in terms of the memref\'s element size, not +the byte lengths required by the intrinsic. + +Out of bounds atomic operations are ignored in hardware. + +See `amdgpu.raw_buffer_load` for a description of how the underlying +instruction is constructed. +""" +function raw_buffer_atomic_cmpswap( + src::Value, + cmp::Value, + memref::Value, + indices::Vector{Value}, + sgprOffset=nothing::Union{Nothing,Value}; + value::IR.Type, + boundsCheck=nothing, + indexOffset=nothing, + location=Location(), +) + _results = IR.Type[value,] + _operands = Value[src, cmp, memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(sgprOffset) && push!(_operands, sgprOffset) + push!( + _attributes, + operandsegmentsizes([1, 1, 1, length(indices), isnothing(sgprOffset) ? 0 : 1]), + ) + !isnothing(boundsCheck) && + push!(_attributes, namedattribute("boundsCheck", boundsCheck)) + !isnothing(indexOffset) && + push!(_attributes, namedattribute("indexOffset", indexOffset)) + + return IR.create_operation( + "amdgpu.raw_buffer_atomic_cmpswap", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_atomic_fadd` + +The `amdgpu.raw_buffer_atomic_fadd` op is a wrapper around the +buffer-based atomic floating point addition available on the MI-* series +of AMD GPUs. + +The index into the buffer is computed as for `memref.store` with the addition +of `indexOffset` (which is used to aid in emitting vectorized code) and, +if present `sgprOffset` (which is added after bounds checks and includes +any non-zero offset on the memref type). + +All indexing components are given in terms of the memref\'s element size, not +the byte lengths required by the intrinsic. + +Out of bounds atomic operations are ignored in hardware. + +See `amdgpu.raw_buffer_load` for a description of how the underlying +instruction is constructed. +""" +function raw_buffer_atomic_fadd( + value::Value, + memref::Value, + indices::Vector{Value}, + sgprOffset=nothing::Union{Nothing,Value}; + boundsCheck=nothing, + indexOffset=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(sgprOffset) && push!(_operands, sgprOffset) + push!( + _attributes, + operandsegmentsizes([1, 1, length(indices), isnothing(sgprOffset) ? 0 : 1]), + ) + !isnothing(boundsCheck) && + push!(_attributes, namedattribute("boundsCheck", boundsCheck)) + !isnothing(indexOffset) && + push!(_attributes, namedattribute("indexOffset", indexOffset)) + + return IR.create_operation( + "amdgpu.raw_buffer_atomic_fadd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_atomic_fmax` + +The `amdgpu.raw_buffer_atomic_fmax` op is a wrapper around the +buffer-based atomic floating point max available on AMD GPUs (except GFX9). + +The index into the buffer is computed as for `memref.store` with the addition +of `indexOffset` (which is used to aid in emitting vectorized code) and, +if present `sgprOffset` (which is added after bounds checks and includes +any non-zero offset on the memref type). + +All indexing components are given in terms of the memref\'s element size, not +the byte lengths required by the intrinsic. + +Out of bounds atomic operations are ignored in hardware. + +See `amdgpu.raw_buffer_load` for a description of how the underlying +instruction is constructed. +""" +function raw_buffer_atomic_fmax( + value::Value, + memref::Value, + indices::Vector{Value}, + sgprOffset=nothing::Union{Nothing,Value}; + boundsCheck=nothing, + indexOffset=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(sgprOffset) && push!(_operands, sgprOffset) + push!( + _attributes, + operandsegmentsizes([1, 1, length(indices), isnothing(sgprOffset) ? 0 : 1]), + ) + !isnothing(boundsCheck) && + push!(_attributes, namedattribute("boundsCheck", boundsCheck)) + !isnothing(indexOffset) && + push!(_attributes, namedattribute("indexOffset", indexOffset)) + + return IR.create_operation( + "amdgpu.raw_buffer_atomic_fmax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_atomic_smax` + +The `amdgpu.raw_buffer_atomic_smax` op is a wrapper around the +buffer-based atomic signed integer max available on AMD GPUs. + +The index into the buffer is computed as for `memref.store` with the addition +of `indexOffset` (which is used to aid in emitting vectorized code) and, +if present `sgprOffset` (which is added after bounds checks and includes +any non-zero offset on the memref type). + +All indexing components are given in terms of the memref\'s element size, not +the byte lengths required by the intrinsic. + +Out of bounds atomic operations are ignored in hardware. + +See `amdgpu.raw_buffer_load` for a description of how the underlying +instruction is constructed. +""" +function raw_buffer_atomic_smax( + value::Value, + memref::Value, + indices::Vector{Value}, + sgprOffset=nothing::Union{Nothing,Value}; + boundsCheck=nothing, + indexOffset=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(sgprOffset) && push!(_operands, sgprOffset) + push!( + _attributes, + operandsegmentsizes([1, 1, length(indices), isnothing(sgprOffset) ? 0 : 1]), + ) + !isnothing(boundsCheck) && + push!(_attributes, namedattribute("boundsCheck", boundsCheck)) + !isnothing(indexOffset) && + push!(_attributes, namedattribute("indexOffset", indexOffset)) + + return IR.create_operation( + "amdgpu.raw_buffer_atomic_smax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_atomic_umin` + +The `amdgpu.raw_buffer_atomic_umin` op is a wrapper around the +buffer-based atomic signed integer min available on AMD GPUs. + +The index into the buffer is computed as for `memref.store` with the addition +of `indexOffset` (which is used to aid in emitting vectorized code) and, +if present `sgprOffset` (which is added after bounds checks and includes +any non-zero offset on the memref type). + +All indexing components are given in terms of the memref\'s element size, not +the byte lengths required by the intrinsic. + +Out of bounds atomic operations are ignored in hardware. + +See `amdgpu.raw_buffer_load` for a description of how the underlying +instruction is constructed. +""" +function raw_buffer_atomic_umin( + value::Value, + memref::Value, + indices::Vector{Value}, + sgprOffset=nothing::Union{Nothing,Value}; + boundsCheck=nothing, + indexOffset=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(sgprOffset) && push!(_operands, sgprOffset) + push!( + _attributes, + operandsegmentsizes([1, 1, length(indices), isnothing(sgprOffset) ? 0 : 1]), + ) + !isnothing(boundsCheck) && + push!(_attributes, namedattribute("boundsCheck", boundsCheck)) + !isnothing(indexOffset) && + push!(_attributes, namedattribute("indexOffset", indexOffset)) + + return IR.create_operation( + "amdgpu.raw_buffer_atomic_umin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_load` + +The `amdgpu.raw_buffer_load` op is a wrapper around the buffer load intrinsics +available on AMD GPUs, including extensions in newer GPUs. + +The index into the buffer is computed as for `memref.load` with the additon +of `indexOffset` and `sgprOffset` (which **may or may not** be considered +in bounds checks and includes any offset present on the memref type if it\'s +non-zero). + +All indices and offsets are in units of the memref\'s data type and are +converted to bytes during lowering. + +When a load is out of bounds, the instruction returns zero. +Partially-out of bounds have chipset-dependent behavior: whether reading +2 elements starting at index 7 of a `memref<8xf32>` returns the last element +in the first vector component depends on the architecture. + +The memref struct is converted into a buffer resource (a V#) and the arguments +are translated to intrinsic arguments as follows: +- The base address of the buffer is the base address of the memref +- The stride is 0 to enable raw mode +- The number of records is the size of the memref, in bytes + In the case of dynamically-shaped memrefs, this is computed at runtime + as max_d (size(d) * stride(d)) * sizeof(elementType(memref)) +- The offset enable bit is 1, the index enable bit is 0. +- The thread ID addition bit is off +- If `boundsCheck` is false and the target chipset is RDNA, OOB_SELECT is set + to 2 to disable bounds checks, otherwise it is 3 +- The cache coherency bits are off +""" +function raw_buffer_load( + memref::Value, + indices::Vector{Value}, + sgprOffset=nothing::Union{Nothing,Value}; + value::IR.Type, + boundsCheck=nothing, + indexOffset=nothing, + location=Location(), +) + _results = IR.Type[value,] + _operands = Value[memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(sgprOffset) && push!(_operands, sgprOffset) + push!( + _attributes, + operandsegmentsizes([1, length(indices), isnothing(sgprOffset) ? 0 : 1]), + ) + !isnothing(boundsCheck) && + push!(_attributes, namedattribute("boundsCheck", boundsCheck)) + !isnothing(indexOffset) && + push!(_attributes, namedattribute("indexOffset", indexOffset)) + + return IR.create_operation( + "amdgpu.raw_buffer_load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_store` + +The `amdgpu.raw_buffer_store` op is a wrapper around the buffer store +intrinsics available on AMD GPUs, including extensions in newer GPUs. + +The store index is computed as in `memref.store` with the addition of +`indexOffset` (which is included for uniformity with atomics and may be useful +when writing vectorized code) and `sgprOffset` (which is added after bounds +checks and implicitly includes the offset of the memref type if non-zero). +All index components are in terms of the elements of the memref, not bytes, +and are scaled up appropriately. + +Out of bounds stores are ignored in hardware. +Wthether a vector write that includes some in-bounds and soeme out-of-bounds +components is partically completed is chipset-dependent. + +See `amdgpu.raw_buffer_load` for a description of how the underlying +instruction is constructed. +""" +function raw_buffer_store( + value::Value, + memref::Value, + indices::Vector{Value}, + sgprOffset=nothing::Union{Nothing,Value}; + boundsCheck=nothing, + indexOffset=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(sgprOffset) && push!(_operands, sgprOffset) + push!( + _attributes, + operandsegmentsizes([1, 1, length(indices), isnothing(sgprOffset) ? 0 : 1]), + ) + !isnothing(boundsCheck) && + push!(_attributes, namedattribute("boundsCheck", boundsCheck)) + !isnothing(indexOffset) && + push!(_attributes, namedattribute("indexOffset", indexOffset)) + + return IR.create_operation( + "amdgpu.raw_buffer_store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wmma` + +The `amdgpu.wmma` op is an MLIR wrapper around intrinsics +for various `wmma` instructions in the RDNA3 architecture, which perform +a 16x16 matrix multiplication for different data types. + +When emitting f16->f16 (or bf16->bf16) wmma the output is a 16xf16 (or 16xbf16) vector +containing only 8 valid values: + - If `subwordOffset` is 0, then the output is stored at indices 0, 2, 4, ..., 14. + - If `subwordOffset` is 1, then the output is stored at indices 1, 3, 5, ..., 15. + +`unsignedA` and `unsignedB` flag that the `int8` LLVM inputs are unsigned. + +The `clamp` flag is used to saturate the output of type T to numeric_limits::max() +in case of overflow. +""" +function wmma( + sourceA::Value, + sourceB::Value, + destC::Value; + destD::IR.Type, + subwordOffset=nothing, + unsignedA=nothing, + unsignedB=nothing, + clamp=nothing, + location=Location(), +) + _results = IR.Type[destD,] + _operands = Value[sourceA, sourceB, destC] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(subwordOffset) && + push!(_attributes, namedattribute("subwordOffset", subwordOffset)) + !isnothing(unsignedA) && push!(_attributes, namedattribute("unsignedA", unsignedA)) + !isnothing(unsignedB) && push!(_attributes, namedattribute("unsignedB", unsignedB)) + !isnothing(clamp) && push!(_attributes, namedattribute("clamp", clamp)) + + return IR.create_operation( + "amdgpu.wmma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # amdgpu diff --git a/src/Dialects/19/AMX.jl b/src/Dialects/19/AMX.jl new file mode 100644 index 00000000..4df6ab3e --- /dev/null +++ b/src/Dialects/19/AMX.jl @@ -0,0 +1,429 @@ +module amx + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`tdpbf16ps` + +""" +function tdpbf16ps( + operand_0::Value, + operand_1::Value, + operand_2::Value, + operand_3::Value, + operand_4::Value, + operand_5::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2, operand_3, operand_4, operand_5] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tdpbf16ps", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tdpbssd` + +""" +function tdpbssd( + operand_0::Value, + operand_1::Value, + operand_2::Value, + operand_3::Value, + operand_4::Value, + operand_5::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2, operand_3, operand_4, operand_5] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tdpbssd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tdpbsud` + +""" +function tdpbsud( + operand_0::Value, + operand_1::Value, + operand_2::Value, + operand_3::Value, + operand_4::Value, + operand_5::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2, operand_3, operand_4, operand_5] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tdpbsud", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tdpbusd` + +""" +function tdpbusd( + operand_0::Value, + operand_1::Value, + operand_2::Value, + operand_3::Value, + operand_4::Value, + operand_5::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2, operand_3, operand_4, operand_5] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tdpbusd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tdpbuud` + +""" +function tdpbuud( + operand_0::Value, + operand_1::Value, + operand_2::Value, + operand_3::Value, + operand_4::Value, + operand_5::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2, operand_3, operand_4, operand_5] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tdpbuud", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tileloadd64` + +""" +function tileloadd64( + operand_0::Value, + operand_1::Value, + operand_2::Value, + operand_3::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2, operand_3] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tileloadd64", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tilestored64` + +""" +function tilestored64( + operand_0::Value, + operand_1::Value, + operand_2::Value, + operand_3::Value, + operand_4::Value; + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand_0, operand_1, operand_2, operand_3, operand_4] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tilestored64", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tilezero` + +""" +function tilezero(operand_0::Value, operand_1::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tilezero", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tile_load` + +Loads a tile from memory defined by a base and indices, with the +shape defined by the 2-dim vector type of the result. This is +eventually lowered into the \"tileloadd\" instruction with the +corresponding tile configuration. + +# Example + +```mlir + %0 = amx.tile_load %arg0[%c0, %c0] : memref into vector<16x64xi8> +``` +""" +function tile_load(base::Value, indices::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[base, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tile_load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tile_mulf` + +Multiplies a \"m x k\" tile with a \"k x n\" tile and accumulates the results +into a \"m x n\" destination tile. Supports \"f32 <- bf16 x bf16\" (with +pairs of \"bf16\"). The operation is eventually lowered into the +\"tdpbf16ps\" instruction with the corresponding tile configuration. + +# Example + +```mlir + %0 = amx.tile_mulf %a, %b, %c + : vector<16x32xbf16>, vector<16x32xbf16>, vector<16x16xf32> +``` +""" +function tile_mulf(lhs::Value, rhs::Value, acc::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, acc] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tile_mulf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tile_muli` + +Multiplies a \"m x k\" tile with a \"k x n\" tile and accumulates the results +into a \"m x n\" destination tile. Supports all \"si32 <- s/ui8 x s/ui8\" +combinations (4 bytes packed into dwords in the columns of both the +source operand tiles; the zero or sign extension is specified with +the attributes and default to sign extended). The operation is eventually +lowered into one of the \"tdpbssd\", \"tdpbsud\", \"tdpbusd\", or \"tdpbuud\" +instructions with the corresponding tile configuration. + +# Example + +```mlir + %0 = amx.tile_muli %a zext, %b zext, %c + : vector<16x64xi8>, vector<16x64xi8>, vector<16x16xi32> +``` +""" +function tile_muli( + lhs::Value, + rhs::Value, + acc::Value; + res::IR.Type, + isZextLhs=nothing, + isZextRhs=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, acc] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(isZextLhs) && push!(_attributes, namedattribute("isZextLhs", isZextLhs)) + !isnothing(isZextRhs) && push!(_attributes, namedattribute("isZextRhs", isZextRhs)) + + return IR.create_operation( + "amx.tile_muli", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tile_store` + +Stores a tile to memory defined by a base and indices, with the +shape defined by the 2-dim vector type of the value. This is +eventually lowered into the \"tilestored\" instruction with the +corresponding tile configuration. + +# Example + +```mlir + amx.tile_store %arg1[%c0, %c0], %0 : memref, vector<16x64xi8> +``` +""" +function tile_store(base::Value, indices::Vector{Value}, val::Value; location=Location()) + _results = IR.Type[] + _operands = Value[base, indices..., val] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tile_store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tile_zero` + +Zeroes the destination tile, with the shape defined by the 2-dim +vector type of the result. This is eventually lowered into the +\"tilezero\" instruction with the corresponding tile configuration. + +# Example + +```mlir + %0 = amx.tile_zero : vector<16x16xbf16> +``` +""" +function tile_zero(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "amx.tile_zero", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # amx diff --git a/src/Dialects/19/Affine.jl b/src/Dialects/19/Affine.jl new file mode 100644 index 00000000..26c8dcd1 --- /dev/null +++ b/src/Dialects/19/Affine.jl @@ -0,0 +1,831 @@ +module affine + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`apply` + +The `affine.apply` operation applies an [affine mapping](#affine-maps) +to a list of SSA values, yielding a single SSA value. The number of +dimension and symbol arguments to `affine.apply` must be equal to the +respective number of dimensional and symbolic inputs to the affine mapping; +the affine mapping has to be one-dimensional, and so the `affine.apply` +operation always returns one value. The input operands and result must all +have ‘index’ type. + +# Example + +```mlir +#map10 = affine_map<(d0, d1) -> (d0 floordiv 8 + d1 floordiv 128)> +... +%1 = affine.apply #map10 (%s, %t) + +// Inline example. +%2 = affine.apply affine_map<(i)[s0] -> (i+s0)> (%42)[%n] +``` +""" +function apply( + mapOperands::Vector{Value}; + result_0=nothing::Union{Nothing,IR.Type}, + map, + location=Location(), +) + _results = IR.Type[] + _operands = Value[mapOperands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("map", map),] + !isnothing(result_0) && push!(_results, result_0) + + return IR.create_operation( + "affine.apply", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`delinearize_index` + +The `affine.delinearize_index` operation takes a single index value and +calculates the multi-index according to the given basis. + +# Example + +``` +%indices:3 = affine.delinearize_index %linear_index into (%c16, %c224, %c224) : index, index, index +``` + +In the above example, `%indices:3` conceptually holds the following: + +``` +#map0 = affine_map<()[s0] -> (s0 floordiv 50176)> +#map1 = affine_map<()[s0] -> ((s0 mod 50176) floordiv 224)> +#map2 = affine_map<()[s0] -> (s0 mod 224)> +%indices_0 = affine.apply #map0()[%linear_index] +%indices_1 = affine.apply #map1()[%linear_index] +%indices_2 = affine.apply #map2()[%linear_index] +``` +""" +function delinearize_index( + linear_index::Value, + basis::Vector{Value}; + multi_index=nothing::Union{Nothing,Vector{IR.Type}}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[linear_index, basis...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(multi_index) && push!(_results, multi_index...) + + return IR.create_operation( + "affine.delinearize_index", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`for_` + +# Syntax + +``` +operation ::= `affine.for` ssa-id `=` lower-bound `to` upper-bound + (`step` integer-literal)? `{` op* `}` + +lower-bound ::= `max`? affine-map-attribute dim-and-symbol-use-list | shorthand-bound +upper-bound ::= `min`? affine-map-attribute dim-and-symbol-use-list | shorthand-bound +shorthand-bound ::= ssa-id | `-`? integer-literal +``` + +The `affine.for` operation represents an affine loop nest. It has one region +containing its body. This region must contain one block that terminates with +[`affine.yield`](#affineyield-mliraffineyieldop). *Note:* when +`affine.for` is printed in custom format, the terminator is omitted. The +block has one argument of [`index`](Builtin.md/#indextype) type that +represents the induction variable of the loop. + +The `affine.for` operation executes its body a number of times iterating +from a lower bound to an upper bound by a stride. The stride, represented by +`step`, is a positive constant integer which defaults to \"1\" if not present. +The lower and upper bounds specify a half-open range: the range includes the +lower bound but does not include the upper bound. + +The lower and upper bounds of a `affine.for` operation are represented as an +application of an affine mapping to a list of SSA values passed to the map. +The [same restrictions](#restrictions-on-dimensions-and-symbols) hold for +these SSA values as for all bindings of SSA values to dimensions and +symbols. + +The affine mappings for the bounds may return multiple results, in which +case the `max`/`min` keywords are required (for the lower/upper bound +respectively), and the bound is the maximum/minimum of the returned values. +There is no semantic ambiguity, but MLIR syntax requires the use of these +keywords to make things more obvious to human readers. + +Many upper and lower bounds are simple, so MLIR accepts two custom form +syntaxes: the form that accepts a single \'ssa-id\' (e.g. `%N`) is shorthand +for applying that SSA value to a function that maps a single symbol to +itself, e.g., `()[s]->(s)()[%N]`. The integer literal form (e.g. `-42`) is +shorthand for a nullary mapping function that returns the constant value +(e.g. `()->(-42)()`). + +Example showing reverse iteration of the inner loop: + +```mlir +#map57 = affine_map<(d0)[s0] -> (s0 - d0 - 1)> + +func.func @simple_example(%A: memref, %B: memref) { + %N = dim %A, 0 : memref + affine.for %i = 0 to %N step 1 { + affine.for %j = 0 to %N { // implicitly steps by 1 + %0 = affine.apply #map57(%j)[%N] + %tmp = call @F1(%A, %i, %0) : (memref, index, index)->(f32) + call @F2(%tmp, %B, %i, %0) : (f32, memref, index, index)->() + } + } + return +} +``` +`affine.for` can also operate on loop-carried variables (`iter_args`) and +return the final values after loop termination. The initial values of the +variables are passed as additional SSA operands to the `affine.for` +following the operands for the loop\'s lower and upper bounds. The +operation\'s region has equivalent arguments for each variable representing +the value of the variable at the current iteration. + +The region must terminate with an `affine.yield` that passes all the current +iteration variables to the next iteration, or to the `affine.for`\'s results +if at the last iteration. For `affine.for`\'s that execute zero iterations, the +initial values of the loop-carried variables (corresponding to the SSA +operands) will be the op\'s results. + +For example, to sum-reduce a memref: + + ```mlir +func.func @reduce(%buffer: memref<1024xf32>) -> (f32) { + // Initial sum set to 0. + %sum_0 = arith.constant 0.0 : f32 + // iter_args binds initial values to the loop\'s region arguments. + %sum = affine.for %i = 0 to 10 step 2 + iter_args(%sum_iter = %sum_0) -> (f32) { + %t = affine.load %buffer[%i] : memref<1024xf32> + %sum_next = arith.addf %sum_iter, %t : f32 + // Yield current iteration sum to next iteration %sum_iter or to %sum + // if final iteration. + affine.yield %sum_next : f32 + } + return %sum : f32 +} +``` + +```mlir +%res:2 = affine.for %i = 0 to 128 iter_args(%arg0 = %init0, %arg1 = %init1) + -> (index, index) { + %y0 = arith.addi %arg0, %c1 : index + %y1 = arith.addi %arg1, %c2 : index + affine.yield %y0, %y1 : index, index +} +``` +If the `affine.for` defines any values, a yield terminator must be +explicitly present. The number and types of the \"affine.for\" results must +match the initial values in the `iter_args` binding and the yield operands. +""" +function for_( + lowerBoundOperands::Vector{Value}, + upperBoundOperands::Vector{Value}, + inits::Vector{Value}; + results::Vector{IR.Type}, + lowerBoundMap, + upperBoundMap, + step, + region::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[lowerBoundOperands..., upperBoundOperands..., inits...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("lowerBoundMap", lowerBoundMap), + namedattribute("upperBoundMap", upperBoundMap), + namedattribute("step", step), + ] + push!( + _attributes, + operandsegmentsizes([ + length(lowerBoundOperands), length(upperBoundOperands), length(inits) + ]), + ) + + return IR.create_operation( + "affine.for", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`if_` + +# Syntax + +``` +operation ::= `affine.if` if-op-cond `{` op* `}` (`else` `{` op* `}`)? +if-op-cond ::= integer-set-attr dim-and-symbol-use-list +``` + +The `affine.if` operation restricts execution to a subset of the loop +iteration space defined by an integer set (a conjunction of affine +constraints). A single `affine.if` may end with an optional `else` clause. + +The condition of the `affine.if` is represented by an +[integer set](#integer-sets) (a conjunction of affine constraints), +and the SSA values bound to the dimensions and symbols in the integer set. +The [same restrictions](#restrictions-on-dimensions-and-symbols) hold for +these SSA values as for all bindings of SSA values to dimensions and +symbols. + +The `affine.if` operation contains two regions for the \"then\" and \"else\" +clauses. `affine.if` may return results that are defined in its regions. +The values defined are determined by which execution path is taken. Each +region of the `affine.if` must contain a single block with no arguments, +and be terminated by `affine.yield`. If `affine.if` defines no values, +the `affine.yield` can be left out, and will be inserted implicitly. +Otherwise, it must be explicit. If no values are defined, the else block +may be empty (i.e. contain no blocks). + +# Example + +```mlir +#set = affine_set<(d0, d1)[s0]: (d0 - 10 >= 0, s0 - d0 - 9 >= 0, + d1 - 10 >= 0, s0 - d1 - 9 >= 0)> +func.func @reduced_domain_example(%A, %X, %N) : (memref<10xi32>, i32, i32) { + affine.for %i = 0 to %N { + affine.for %j = 0 to %N { + %0 = affine.apply #map42(%j) + %tmp = call @S1(%X, %i, %0) + affine.if #set(%i, %j)[%N] { + %1 = affine.apply #map43(%i, %j) + call @S2(%tmp, %A, %i, %1) + } + } + } + return +} +``` + +Example with an explicit yield (initialization with edge padding): + +```mlir +#interior = affine_set<(i, j) : (i - 1 >= 0, j - 1 >= 0, 10 - i >= 0, 10 - j >= 0)> (%i, %j) +func.func @pad_edges(%I : memref<10x10xf32>) -> (memref<12x12xf32) { + %O = alloc memref<12x12xf32> + affine.parallel (%i, %j) = (0, 0) to (12, 12) { + %1 = affine.if #interior (%i, %j) { + %2 = load %I[%i - 1, %j - 1] : memref<10x10xf32> + affine.yield %2 + } else { + %2 = arith.constant 0.0 : f32 + affine.yield %2 : f32 + } + affine.store %1, %O[%i, %j] : memref<12x12xf32> + } + return %O +} +``` +""" +function if_( + operand_0::Vector{Value}; + results::Vector{IR.Type}, + thenRegion::Region, + elseRegion::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[operand_0...,] + _owned_regions = Region[thenRegion, elseRegion] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "affine.if", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`load` + +# Syntax + +``` +operation ::= ssa-id `=` `affine.load` ssa-use `[` multi-dim-affine-map-of-ssa-ids `]` `:` memref-type +``` + +The `affine.load` op reads an element from a memref, where the index +for each memref dimension is an affine expression of loop induction +variables and symbols. The output of `affine.load` is a new value with the +same type as the elements of the memref. An affine expression of loop IVs +and symbols must be specified for each dimension of the memref. The keyword +`symbol` can be used to indicate SSA identifiers which are symbolic. + +Example 1: + +```mlir +%1 = affine.load %0[%i0 + 3, %i1 + 7] : memref<100x100xf32> +``` + +Example 2: Uses `symbol` keyword for symbols `%n` and `%m`. + +```mlir +%1 = affine.load %0[%i0 + symbol(%n), %i1 + symbol(%m)] : memref<100x100xf32> +``` +""" +function load( + memref::Value, indices::Vector{Value}; result::IR.Type, map, location=Location() +) + _results = IR.Type[result,] + _operands = Value[memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("map", map),] + + return IR.create_operation( + "affine.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`max` + +The `affine.max` operation computes the maximum value result from a multi-result +affine map. + +# Example + +```mlir +%0 = affine.max (d0) -> (1000, d0 + 512) (%i0) : index +``` +""" +function max( + operands::Vector{Value}; + result_0=nothing::Union{Nothing,IR.Type}, + map, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("map", map),] + !isnothing(result_0) && push!(_results, result_0) + + return IR.create_operation( + "affine.max", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`min` + +# Syntax + +``` +operation ::= ssa-id `=` `affine.min` affine-map-attribute dim-and-symbol-use-list +``` + +The `affine.min` operation applies an [affine mapping](#affine-expressions) +to a list of SSA values, and returns the minimum value of all result +expressions. The number of dimension and symbol arguments to `affine.min` +must be equal to the respective number of dimensional and symbolic inputs to +the affine mapping; the `affine.min` operation always returns one value. The +input operands and result must all have \'index\' type. + +# Example + +```mlir +%0 = affine.min affine_map<(d0)[s0] -> (1000, d0 + 512, s0)> (%arg0)[%arg1] +``` +""" +function min( + operands::Vector{Value}; + result_0=nothing::Union{Nothing,IR.Type}, + map, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("map", map),] + !isnothing(result_0) && push!(_results, result_0) + + return IR.create_operation( + "affine.min", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`parallel` + +The `affine.parallel` operation represents a hyper-rectangular affine +parallel band, defining zero or more SSA values for its induction variables. +It has one region capturing the parallel band body. The induction variables +are represented as arguments of this region. These SSA values always have +type index, which is the size of the machine word. The strides, represented +by steps, are positive constant integers which defaults to \"1\" if not +present. The lower and upper bounds specify a half-open range: the range +includes the lower bound but does not include the upper bound. The body +region must contain exactly one block that terminates with `affine.yield`. + +The lower and upper bounds of a parallel operation are represented as an +application of an affine mapping to a list of SSA values passed to the map. +The same restrictions hold for these SSA values as for all bindings of SSA +values to dimensions and symbols. The list of expressions in each map is +interpreted according to the respective bounds group attribute. If a single +expression belongs to the group, then the result of this expression is taken +as a lower(upper) bound of the corresponding loop induction variable. If +multiple expressions belong to the group, then the lower(upper) bound is the +max(min) of these values obtained from these expressions. The loop band has +as many loops as elements in the group bounds attributes. + +Each value yielded by `affine.yield` will be accumulated/reduced via one of +the reduction methods defined in the AtomicRMWKind enum. The order of +reduction is unspecified, and lowering may produce any valid ordering. +Loops with a 0 trip count will produce as a result the identity value +associated with each reduction (i.e. 0.0 for addf, 1.0 for mulf). Assign +reductions for loops with a trip count != 1 produces undefined results. + +Note: Calling `AffineParallelOp::build` will create the required region and +block, and insert the required terminator if it is trivial (i.e. no values +are yielded). Parsing will also create the required region, block, and +terminator, even when they are missing from the textual representation. + +Example (3x3 valid convolution): + +```mlir +func.func @conv_2d(%D : memref<100x100xf32>, %K : memref<3x3xf32>) -> (memref<98x98xf32>) { + %O = memref.alloc() : memref<98x98xf32> + affine.parallel (%x, %y) = (0, 0) to (98, 98) { + %0 = affine.parallel (%kx, %ky) = (0, 0) to (2, 2) reduce (\"addf\") -> f32 { + %1 = affine.load %D[%x + %kx, %y + %ky] : memref<100x100xf32> + %2 = affine.load %K[%kx, %ky] : memref<3x3xf32> + %3 = arith.mulf %1, %2 : f32 + affine.yield %3 : f32 + } + affine.store %0, %O[%x, %y] : memref<98x98xf32> + } + return %O : memref<98x98xf32> +} +``` + +Example (tiling by potentially imperfectly dividing sizes): + +```mlir +affine.parallel (%ii, %jj) = (0, 0) to (%N, %M) step (32, 32) { + affine.parallel (%i, %j) = (%ii, %jj) + to (min(%ii + 32, %N), min(%jj + 32, %M)) { + call @f(%i, %j) : (index, index) -> () + } +} +``` +""" +function parallel( + mapOperands::Vector{Value}; + results::Vector{IR.Type}, + reductions, + lowerBoundsMap, + lowerBoundsGroups, + upperBoundsMap, + upperBoundsGroups, + steps, + region::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[mapOperands...,] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("reductions", reductions), + namedattribute("lowerBoundsMap", lowerBoundsMap), + namedattribute("lowerBoundsGroups", lowerBoundsGroups), + namedattribute("upperBoundsMap", upperBoundsMap), + namedattribute("upperBoundsGroups", upperBoundsGroups), + namedattribute("steps", steps), + ] + + return IR.create_operation( + "affine.parallel", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`prefetch` + +The `affine.prefetch` op prefetches data from a memref location described +with an affine subscript similar to affine.load, and has three attributes: +a read/write specifier, a locality hint, and a cache type specifier as shown +below: + +```mlir +affine.prefetch %0[%i, %j + 5], read, locality<3>, data : memref<400x400xi32> +``` + +The read/write specifier is either \'read\' or \'write\', the locality hint +specifier ranges from locality<0> (no locality) to locality<3> (extremely +local keep in cache). The cache type specifier is either \'data\' or \'instr\' +and specifies whether the prefetch is performed on data cache or on +instruction cache. +""" +function prefetch( + memref::Value, + indices::Vector{Value}; + isWrite, + localityHint, + isDataCache, + map, + location=Location(), +) + _results = IR.Type[] + _operands = Value[memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("isWrite", isWrite), + namedattribute("localityHint", localityHint), + namedattribute("isDataCache", isDataCache), + namedattribute("map", map), + ] + + return IR.create_operation( + "affine.prefetch", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`store` + +# Syntax + +``` +operation ::= `affine.store` ssa-use, ssa-use `[` multi-dim-affine-map-of-ssa-ids `]` `:` memref-type +``` + +The `affine.store` op writes an element to a memref, where the index +for each memref dimension is an affine expression of loop induction +variables and symbols. The `affine.store` op stores a new value which is the +same type as the elements of the memref. An affine expression of loop IVs +and symbols must be specified for each dimension of the memref. The keyword +`symbol` can be used to indicate SSA identifiers which are symbolic. + +Example 1: + +```mlir +affine.store %v0, %0[%i0 + 3, %i1 + 7] : memref<100x100xf32> +``` + +Example 2: Uses `symbol` keyword for symbols `%n` and `%m`. + +```mlir +affine.store %v0, %0[%i0 + symbol(%n), %i1 + symbol(%m)] : memref<100x100xf32> +``` +""" +function store( + value::Value, memref::Value, indices::Vector{Value}; map, location=Location() +) + _results = IR.Type[] + _operands = Value[value, memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("map", map),] + + return IR.create_operation( + "affine.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`vector_load` + +The `affine.vector_load` is the vector counterpart of +[affine.load](#affineload-mliraffineloadop). It reads a slice from a +[MemRef](Builtin.md/#memreftype), supplied as its first operand, +into a [vector](Builtin.md/#vectortype) of the same base elemental type. +The index for each memref dimension is an affine expression of loop induction +variables and symbols. These indices determine the start position of the read +within the memref. The shape of the return vector type determines the shape of +the slice read from the memref. This slice is contiguous along the respective +dimensions of the shape. Strided vector loads will be supported in the future. +An affine expression of loop IVs and symbols must be specified for each +dimension of the memref. The keyword `symbol` can be used to indicate SSA +identifiers which are symbolic. + +Example 1: 8-wide f32 vector load. + +```mlir +%1 = affine.vector_load %0[%i0 + 3, %i1 + 7] : memref<100x100xf32>, vector<8xf32> +``` + +Example 2: 4-wide f32 vector load. Uses `symbol` keyword for symbols `%n` and `%m`. + +```mlir +%1 = affine.vector_load %0[%i0 + symbol(%n), %i1 + symbol(%m)] : memref<100x100xf32>, vector<4xf32> +``` + +Example 3: 2-dim f32 vector load. + +```mlir +%1 = affine.vector_load %0[%i0, %i1] : memref<100x100xf32>, vector<2x8xf32> +``` + +TODOs: +* Add support for strided vector loads. +* Consider adding a permutation map to permute the slice that is read from memory +(see [vector.transfer_read](../Vector/#vectortransfer_read-mlirvectortransferreadop)). +""" +function vector_load( + memref::Value, indices::Vector{Value}; result::IR.Type, map, location=Location() +) + _results = IR.Type[result,] + _operands = Value[memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("map", map),] + + return IR.create_operation( + "affine.vector_load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`vector_store` + +The `affine.vector_store` is the vector counterpart of +[affine.store](#affinestore-mliraffinestoreop). It writes a +[vector](Builtin.md/#vectortype), supplied as its first operand, +into a slice within a [MemRef](Builtin.md/#memreftype) of the same base +elemental type, supplied as its second operand. +The index for each memref dimension is an affine expression of loop +induction variables and symbols. These indices determine the start position +of the write within the memref. The shape of th input vector determines the +shape of the slice written to the memref. This slice is contiguous along the +respective dimensions of the shape. Strided vector stores will be supported +in the future. +An affine expression of loop IVs and symbols must be specified for each +dimension of the memref. The keyword `symbol` can be used to indicate SSA +identifiers which are symbolic. + +Example 1: 8-wide f32 vector store. + +```mlir +affine.vector_store %v0, %0[%i0 + 3, %i1 + 7] : memref<100x100xf32>, vector<8xf32> +``` + +Example 2: 4-wide f32 vector store. Uses `symbol` keyword for symbols `%n` and `%m`. + +```mlir +affine.vector_store %v0, %0[%i0 + symbol(%n), %i1 + symbol(%m)] : memref<100x100xf32>, vector<4xf32> +``` + +Example 3: 2-dim f32 vector store. + +```mlir +affine.vector_store %v0, %0[%i0, %i1] : memref<100x100xf32>, vector<2x8xf32> +``` + +TODOs: +* Add support for strided vector stores. +* Consider adding a permutation map to permute the slice that is written to memory +(see [vector.transfer_write](../Vector/#vectortransfer_write-mlirvectortransferwriteop)). +""" +function vector_store( + value::Value, memref::Value, indices::Vector{Value}; map, location=Location() +) + _results = IR.Type[] + _operands = Value[value, memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("map", map),] + + return IR.create_operation( + "affine.vector_store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +The `affine.yield` yields zero or more SSA values from an affine op region and +terminates the region. The semantics of how the values yielded are used +is defined by the parent operation. +If `affine.yield` has any operands, the operands must match the parent +operation\'s results. +If the parent operation defines no values, then the `affine.yield` may be +left out in the custom syntax and the builders will insert one implicitly. +Otherwise, it has to be present in the syntax to indicate which values are +yielded. +""" +function yield(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "affine.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # affine diff --git a/src/Dialects/19/Arith.jl b/src/Dialects/19/Arith.jl new file mode 100644 index 00000000..6c5765c6 --- /dev/null +++ b/src/Dialects/19/Arith.jl @@ -0,0 +1,2041 @@ +module arith + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`addf` + +The `addf` operation takes two operands and returns one result, each of +these is required to be the same type. This type may be a floating point +scalar type, a vector whose element type is a floating point type, or a +floating point tensor. + +# Example + +```mlir +// Scalar addition. +%a = arith.addf %b, %c : f64 + +// SIMD vector addition, e.g. for Intel SSE. +%f = arith.addf %g, %h : vector<4xf32> + +// Tensor addition. +%x = arith.addf %y, %z : tensor<4x?xbf16> +``` + +TODO: In the distant future, this will accept optional attributes for fast +math, contraction, rounding mode, and other controls. +""" +function addf( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.addf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`addi` + +Performs N-bit addition on the operands. The operands are interpreted as +unsigned bitvectors. The result is represented by a bitvector containing the +mathematical value of the addition modulo 2^n, where `n` is the bitwidth. +Because `arith` integers use a two\'s complement representation, this operation +is applicable on both signed and unsigned integer operands. + +The `addi` operation takes two operands and returns one result, each of +these is required to be the same type. This type may be an integer scalar type, +a vector whose element type is integer, or a tensor of integers. + +This op supports `nuw`/`nsw` overflow flags which stands stand for +\"No Unsigned Wrap\" and \"No Signed Wrap\", respectively. If the `nuw` and/or +`nsw` flags are present, and an unsigned/signed overflow occurs +(respectively), the result is poison. + +# Example + +```mlir +// Scalar addition. +%a = arith.addi %b, %c : i64 + +// Scalar addition with overflow flags. +%a = arith.addi %b, %c overflow : i64 + +// SIMD vector element-wise addition. +%f = arith.addi %g, %h : vector<4xi32> + +// Tensor element-wise addition. +%x = arith.addi %y, %z : tensor<4x?xi8> +``` +""" +function addi( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + overflowFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(overflowFlags) && + push!(_attributes, namedattribute("overflowFlags", overflowFlags)) + + return IR.create_operation( + "arith.addi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`addui_extended` + +Performs (N+1)-bit addition on zero-extended operands. Returns two results: +the N-bit sum (same type as both operands), and the overflow bit +(boolean-like), where `1` indicates unsigned addition overflow, while `0` +indicates no overflow. + +# Example + +```mlir +// Scalar addition. +%sum, %overflow = arith.addui_extended %b, %c : i64, i1 + +// Vector element-wise addition. +%d:2 = arith.addui_extended %e, %f : vector<4xi32>, vector<4xi1> + +// Tensor element-wise addition. +%x:2 = arith.addui_extended %y, %z : tensor<4x?xi8>, tensor<4x?xi1> +``` +""" +function addui_extended( + lhs::Value, rhs::Value; sum::IR.Type, overflow::IR.Type, location=Location() +) + _results = IR.Type[sum, overflow] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arith.addui_extended", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`andi` + +The `andi` operation takes two operands and returns one result, each of +these is required to be the same type. This type may be an integer scalar +type, a vector whose element type is integer, or a tensor of integers. It +has no standard attributes. + +# Example + +```mlir +// Scalar integer bitwise and. +%a = arith.andi %b, %c : i64 + +// SIMD vector element-wise bitwise integer and. +%f = arith.andi %g, %h : vector<4xi32> + +// Tensor element-wise bitwise integer and. +%x = arith.andi %y, %z : tensor<4x?xi8> +``` +""" +function andi( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.andi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`bitcast` + +Bitcast an integer or floating point value to an integer or floating point +value of equal bit width. When operating on vectors, casts elementwise. + +Note that this implements a logical bitcast independent of target +endianness. This allows constant folding without target information and is +consitent with the bitcast constant folders in LLVM (see +https://github.com/llvm/llvm-project/blob/18c19414eb/llvm/lib/IR/ConstantFold.cpp#L168) +For targets where the source and target type have the same endianness (which +is the standard), this cast will also change no bits at runtime, but it may +still require an operation, for example if the machine has different +floating point and integer register files. For targets that have a different +endianness for the source and target types (e.g. float is big-endian and +integer is little-endian) a proper lowering would add operations to swap the +order of words in addition to the bitcast. +""" +function bitcast(in::Value; out::IR.Type, location=Location()) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arith.bitcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ceildivsi` + +Signed integer division. Rounds towards positive infinity, i.e. `7 / -2 = -3`. + +Divison by zero, or signed division overflow (minimum value divided by -1) +is undefined behavior. When applied to `vector` and `tensor` values, the +behavior is undefined if _any_ of its elements are divided by zero or has a +signed division overflow. + +# Example + +```mlir +// Scalar signed integer division. +%a = arith.ceildivsi %b, %c : i64 +``` +""" +function ceildivsi( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.ceildivsi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ceildivui` + +Unsigned integer division. Rounds towards positive infinity. Treats the +leading bit as the most significant, i.e. for `i16` given two\'s complement +representation, `6 / -2 = 6 / (2^16 - 2) = 1`. + +Division by zero is undefined behavior. When applied to `vector` and +`tensor` values, the behavior is undefined if _any_ elements are divided by +zero. + +# Example + +```mlir +// Scalar unsigned integer division. +%a = arith.ceildivui %b, %c : i64 +``` +""" +function ceildivui( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.ceildivui", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cmpf` + +The `cmpf` operation compares its two operands according to the float +comparison rules and the predicate specified by the respective attribute. +The predicate defines the type of comparison: (un)orderedness, (in)equality +and signed less/greater than (or equal to) as well as predicates that are +always true or false. The operands must have the same type, and this type +must be a float type, or a vector or tensor thereof. The result is an i1, +or a vector/tensor thereof having the same shape as the inputs. Unlike cmpi, +the operands are always treated as signed. The u prefix indicates +*unordered* comparison, not unsigned comparison, so \"une\" means unordered or +not equal. For the sake of readability by humans, custom assembly form for +the operation uses a string-typed attribute for the predicate. The value of +this attribute corresponds to lower-cased name of the predicate constant, +e.g., \"one\" means \"ordered not equal\". The string representation of the +attribute is merely a syntactic sugar and is converted to an integer +attribute by the parser. + +# Example + +```mlir +%r1 = arith.cmpf oeq, %0, %1 : f32 +%r2 = arith.cmpf ult, %0, %1 : tensor<42x42xf64> +%r3 = \"arith.cmpf\"(%0, %1) {predicate: 0} : (f8, f8) -> i1 +``` +""" +function cmpf( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + predicate, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("predicate", predicate),] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.cmpf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cmpi` + +The `cmpi` operation is a generic comparison for integer-like types. Its two +arguments can be integers, vectors or tensors thereof as long as their types +match. The operation produces an i1 for the former case, a vector or a +tensor of i1 with the same shape as inputs in the other cases. + +Its first argument is an attribute that defines which type of comparison is +performed. The following comparisons are supported: + +- equal (mnemonic: `\"eq\"`; integer value: `0`) +- not equal (mnemonic: `\"ne\"`; integer value: `1`) +- signed less than (mnemonic: `\"slt\"`; integer value: `2`) +- signed less than or equal (mnemonic: `\"sle\"`; integer value: `3`) +- signed greater than (mnemonic: `\"sgt\"`; integer value: `4`) +- signed greater than or equal (mnemonic: `\"sge\"`; integer value: `5`) +- unsigned less than (mnemonic: `\"ult\"`; integer value: `6`) +- unsigned less than or equal (mnemonic: `\"ule\"`; integer value: `7`) +- unsigned greater than (mnemonic: `\"ugt\"`; integer value: `8`) +- unsigned greater than or equal (mnemonic: `\"uge\"`; integer value: `9`) + +The result is `1` if the comparison is true and `0` otherwise. For vector or +tensor operands, the comparison is performed elementwise and the element of +the result indicates whether the comparison is true for the operand elements +with the same indices as those of the result. + +Note: while the custom assembly form uses strings, the actual underlying +attribute has integer type (or rather enum class in C++ code) as seen from +the generic assembly form. String literals are used to improve readability +of the IR by humans. + +This operation only applies to integer-like operands, but not floats. The +main reason being that comparison operations have diverging sets of +attributes: integers require sign specification while floats require various +floating point-related particularities, e.g., `-ffast-math` behavior, +IEEE754 compliance, etc +([rationale](../Rationale/Rationale.md#splitting-floating-point-vs-integer-operations)). +The type of comparison is specified as attribute to avoid introducing ten +similar operations, taking into account that they are often implemented +using the same operation downstream +([rationale](../Rationale/Rationale.md#specifying-comparison-kind-as-attribute)). The +separation between signed and unsigned order comparisons is necessary +because of integers being signless. The comparison operation must know how +to interpret values with the foremost bit being set: negatives in two\'s +complement or large positives +([rationale](../Rationale/Rationale.md#specifying-sign-in-integer-comparison-operations)). + +# Example + +```mlir +// Custom form of scalar \"signed less than\" comparison. +%x = arith.cmpi slt, %lhs, %rhs : i32 + +// Generic form of the same operation. +%x = \"arith.cmpi\"(%lhs, %rhs) {predicate = 2 : i64} : (i32, i32) -> i1 + +// Custom form of vector equality comparison. +%x = arith.cmpi eq, %lhs, %rhs : vector<4xi64> + +// Generic form of the same operation. +%x = \"arith.cmpi\"(%lhs, %rhs) {predicate = 0 : i64} + : (vector<4xi64>, vector<4xi64>) -> vector<4xi1> +``` +""" +function cmpi( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + predicate, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("predicate", predicate),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.cmpi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`constant` + +The `constant` operation produces an SSA value equal to some integer or +floating-point constant specified by an attribute. This is the way MLIR +forms simple integer and floating point constants. + +# Example + +``` +// Integer constant +%1 = arith.constant 42 : i32 + +// Equivalent generic form +%1 = \"arith.constant\"() {value = 42 : i32} : () -> i32 +``` +""" +function constant(; result=nothing::Union{Nothing,IR.Type}, value, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`divf` + +""" +function divf( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.divf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`divsi` + +Signed integer division. Rounds towards zero. Treats the leading bit as +sign, i.e. `6 / -2 = -3`. + +Divison by zero, or signed division overflow (minimum value divided by -1) +is undefined behavior. When applied to `vector` and `tensor` values, the +behavior is undefined if _any_ of its elements are divided by zero or has a +signed division overflow. + +# Example + +```mlir +// Scalar signed integer division. +%a = arith.divsi %b, %c : i64 + +// SIMD vector element-wise division. +%f = arith.divsi %g, %h : vector<4xi32> + +// Tensor element-wise integer division. +%x = arith.divsi %y, %z : tensor<4x?xi8> +``` +""" +function divsi( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.divsi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`divui` + +Unsigned integer division. Rounds towards zero. Treats the leading bit as +the most significant, i.e. for `i16` given two\'s complement representation, +`6 / -2 = 6 / (2^16 - 2) = 0`. + +Division by zero is undefined behavior. When applied to `vector` and +`tensor` values, the behavior is undefined if _any_ elements are divided by +zero. + +# Example + +```mlir +// Scalar unsigned integer division. +%a = arith.divui %b, %c : i64 + +// SIMD vector element-wise division. +%f = arith.divui %g, %h : vector<4xi32> + +// Tensor element-wise integer division. +%x = arith.divui %y, %z : tensor<4x?xi8> +``` +""" +function divui( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.divui", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`extf` + +Cast a floating-point value to a larger floating-point-typed value. +The destination type must to be strictly wider than the source type. +When operating on vectors, casts elementwise. +""" +function extf(in::Value; out::IR.Type, fastmath=nothing, location=Location()) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.extf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`extsi` + +The integer sign extension operation takes an integer input of +width M and an integer destination type of width N. The destination +bit-width must be larger than the input bit-width (N > M). +The top-most (N - M) bits of the output are filled with copies +of the most-significant bit of the input. + +# Example + +```mlir +%1 = arith.constant 5 : i3 // %1 is 0b101 +%2 = arith.extsi %1 : i3 to i6 // %2 is 0b111101 +%3 = arith.constant 2 : i3 // %3 is 0b010 +%4 = arith.extsi %3 : i3 to i6 // %4 is 0b000010 + +%5 = arith.extsi %0 : vector<2 x i32> to vector<2 x i64> +``` +""" +function extsi(in::Value; out::IR.Type, location=Location()) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arith.extsi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`extui` + +The integer zero extension operation takes an integer input of +width M and an integer destination type of width N. The destination +bit-width must be larger than the input bit-width (N > M). +The top-most (N - M) bits of the output are filled with zeros. + +# Example + +```mlir + %1 = arith.constant 5 : i3 // %1 is 0b101 + %2 = arith.extui %1 : i3 to i6 // %2 is 0b000101 + %3 = arith.constant 2 : i3 // %3 is 0b010 + %4 = arith.extui %3 : i3 to i6 // %4 is 0b000010 + + %5 = arith.extui %0 : vector<2 x i32> to vector<2 x i64> +``` +""" +function extui(in::Value; out::IR.Type, location=Location()) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arith.extui", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fptosi` + +Cast from a value interpreted as floating-point to the nearest (rounding +towards zero) signed integer value. When operating on vectors, casts +elementwise. +""" +function fptosi(in::Value; out::IR.Type, location=Location()) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arith.fptosi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fptoui` + +Cast from a value interpreted as floating-point to the nearest (rounding +towards zero) unsigned integer value. When operating on vectors, casts +elementwise. +""" +function fptoui(in::Value; out::IR.Type, location=Location()) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arith.fptoui", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`floordivsi` + +Signed integer division. Rounds towards negative infinity, i.e. `5 / -2 = -3`. + +Divison by zero, or signed division overflow (minimum value divided by -1) +is undefined behavior. When applied to `vector` and `tensor` values, the +behavior is undefined if _any_ of its elements are divided by zero or has a +signed division overflow. + +# Example + +```mlir +// Scalar signed integer division. +%a = arith.floordivsi %b, %c : i64 + +``` +""" +function floordivsi( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.floordivsi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`index_cast` + +Casts between scalar or vector integers and corresponding \'index\' scalar or +vectors. Index is an integer of platform-specific bit width. If casting to +a wider integer, the value is sign-extended. If casting to a narrower +integer, the value is truncated. +""" +function index_cast(in::Value; out::IR.Type, location=Location()) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arith.index_cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`index_castui` + +Casts between scalar or vector integers and corresponding \'index\' scalar or +vectors. Index is an integer of platform-specific bit width. If casting to +a wider integer, the value is zero-extended. If casting to a narrower +integer, the value is truncated. +""" +function index_castui(in::Value; out::IR.Type, location=Location()) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arith.index_castui", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`maxnumf` + +Returns the maximum of the two arguments. +If the arguments are -0.0 and +0.0, then the result is either of them. +If one of the arguments is NaN, then the result is the other argument. + +# Example + +```mlir +// Scalar floating-point maximum. +%a = arith.maxnumf %b, %c : f64 +``` +""" +function maxnumf( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.maxnumf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`maxsi` + +""" +function maxsi( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.maxsi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`maxui` + +""" +function maxui( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.maxui", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`maximumf` + +Returns the maximum of the two arguments, treating -0.0 as less than +0.0. +If one of the arguments is NaN, then the result is also NaN. + +# Example + +```mlir +// Scalar floating-point maximum. +%a = arith.maximumf %b, %c : f64 +``` +""" +function maximumf( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.maximumf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`minnumf` + +Returns the minimum of the two arguments. +If the arguments are -0.0 and +0.0, then the result is either of them. +If one of the arguments is NaN, then the result is the other argument. + +# Example + +```mlir +// Scalar floating-point minimum. +%a = arith.minnumf %b, %c : f64 +``` +""" +function minnumf( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.minnumf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`minsi` + +""" +function minsi( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.minsi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`minui` + +""" +function minui( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.minui", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`minimumf` + +Returns the minimum of the two arguments, treating -0.0 as less than +0.0. +If one of the arguments is NaN, then the result is also NaN. + +# Example + +```mlir +// Scalar floating-point minimum. +%a = arith.minimumf %b, %c : f64 +``` +""" +function minimumf( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.minimumf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mulf` + +The `mulf` operation takes two operands and returns one result, each of +these is required to be the same type. This type may be a floating point +scalar type, a vector whose element type is a floating point type, or a +floating point tensor. + +# Example + +```mlir +// Scalar multiplication. +%a = arith.mulf %b, %c : f64 + +// SIMD pointwise vector multiplication, e.g. for Intel SSE. +%f = arith.mulf %g, %h : vector<4xf32> + +// Tensor pointwise multiplication. +%x = arith.mulf %y, %z : tensor<4x?xbf16> +``` + +TODO: In the distant future, this will accept optional attributes for fast +math, contraction, rounding mode, and other controls. +""" +function mulf( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.mulf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`muli` + +Performs N-bit multiplication on the operands. The operands are interpreted as +unsigned bitvectors. The result is represented by a bitvector containing the +mathematical value of the multiplication modulo 2^n, where `n` is the bitwidth. +Because `arith` integers use a two\'s complement representation, this operation is +applicable on both signed and unsigned integer operands. + +The `muli` operation takes two operands and returns one result, each of +these is required to be the same type. This type may be an integer scalar type, +a vector whose element type is integer, or a tensor of integers. + +This op supports `nuw`/`nsw` overflow flags which stands stand for +\"No Unsigned Wrap\" and \"No Signed Wrap\", respectively. If the `nuw` and/or +`nsw` flags are present, and an unsigned/signed overflow occurs +(respectively), the result is poison. + +# Example + +```mlir +// Scalar multiplication. +%a = arith.muli %b, %c : i64 + +// Scalar multiplication with overflow flags. +%a = arith.muli %b, %c overflow : i64 + +// SIMD vector element-wise multiplication. +%f = arith.muli %g, %h : vector<4xi32> + +// Tensor element-wise multiplication. +%x = arith.muli %y, %z : tensor<4x?xi8> +``` +""" +function muli( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + overflowFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(overflowFlags) && + push!(_attributes, namedattribute("overflowFlags", overflowFlags)) + + return IR.create_operation( + "arith.muli", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mulsi_extended` + +Performs (2*N)-bit multiplication on sign-extended operands. Returns two +N-bit results: the low and the high halves of the product. The low half has +the same value as the result of regular multiplication `arith.muli` with +the same operands. + +# Example + +```mlir +// Scalar multiplication. +%low, %high = arith.mulsi_extended %a, %b : i32 + +// Vector element-wise multiplication. +%c:2 = arith.mulsi_extended %d, %e : vector<4xi32> + +// Tensor element-wise multiplication. +%x:2 = arith.mulsi_extended %y, %z : tensor<4x?xi8> +``` +""" +function mulsi_extended( + lhs::Value, + rhs::Value; + low=nothing::Union{Nothing,IR.Type}, + high=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(low) && push!(_results, low) + !isnothing(high) && push!(_results, high) + + return IR.create_operation( + "arith.mulsi_extended", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mului_extended` + +Performs (2*N)-bit multiplication on zero-extended operands. Returns two +N-bit results: the low and the high halves of the product. The low half has +the same value as the result of regular multiplication `arith.muli` with +the same operands. + +# Example + +```mlir +// Scalar multiplication. +%low, %high = arith.mului_extended %a, %b : i32 + +// Vector element-wise multiplication. +%c:2 = arith.mului_extended %d, %e : vector<4xi32> + +// Tensor element-wise multiplication. +%x:2 = arith.mului_extended %y, %z : tensor<4x?xi8> +``` +""" +function mului_extended( + lhs::Value, + rhs::Value; + low=nothing::Union{Nothing,IR.Type}, + high=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(low) && push!(_results, low) + !isnothing(high) && push!(_results, high) + + return IR.create_operation( + "arith.mului_extended", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`negf` + +The `negf` operation computes the negation of a given value. It takes one +operand and returns one result of the same type. This type may be a float +scalar type, a vector whose element type is float, or a tensor of floats. +It has no standard attributes. + +# Example + +```mlir +// Scalar negation value. +%a = arith.negf %b : f64 + +// SIMD vector element-wise negation value. +%f = arith.negf %g : vector<4xf32> + +// Tensor element-wise negation value. +%x = arith.negf %y : tensor<4x?xf8> +``` +""" +function negf( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.negf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ori` + +The `ori` operation takes two operands and returns one result, each of these +is required to be the same type. This type may be an integer scalar type, a +vector whose element type is integer, or a tensor of integers. It has no +standard attributes. + +# Example + +```mlir +// Scalar integer bitwise or. +%a = arith.ori %b, %c : i64 + +// SIMD vector element-wise bitwise integer or. +%f = arith.ori %g, %h : vector<4xi32> + +// Tensor element-wise bitwise integer or. +%x = arith.ori %y, %z : tensor<4x?xi8> +``` +""" +function ori( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.ori", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`remf` + +Returns the floating point division remainder. +The remainder has the same sign as the dividend (lhs operand). +""" +function remf( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.remf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`remsi` + +Signed integer division remainder. Treats the leading bit as sign, i.e. `6 % +-2 = 0`. + +Division by zero is undefined behavior. When applied to `vector` and +`tensor` values, the behavior is undefined if _any_ elements are divided by +zero. + +# Example + +```mlir +// Scalar signed integer division remainder. +%a = arith.remsi %b, %c : i64 + +// SIMD vector element-wise division remainder. +%f = arith.remsi %g, %h : vector<4xi32> + +// Tensor element-wise integer division remainder. +%x = arith.remsi %y, %z : tensor<4x?xi8> +``` +""" +function remsi( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.remsi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`remui` + +Unsigned integer division remainder. Treats the leading bit as the most +significant, i.e. for `i16`, `6 % -2 = 6 % (2^16 - 2) = 6`. + +Division by zero is undefined behavior. When applied to `vector` and +`tensor` values, the behavior is undefined if _any_ elements are divided by +zero. + +# Example + +```mlir +// Scalar unsigned integer division remainder. +%a = arith.remui %b, %c : i64 + +// SIMD vector element-wise division remainder. +%f = arith.remui %g, %h : vector<4xi32> + +// Tensor element-wise integer division remainder. +%x = arith.remui %y, %z : tensor<4x?xi8> +``` +""" +function remui( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.remui", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sitofp` + +Cast from a value interpreted as a signed integer to the corresponding +floating-point value. If the value cannot be exactly represented, it is +rounded using the default rounding mode. When operating on vectors, casts +elementwise. +""" +function sitofp(in::Value; out::IR.Type, location=Location()) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arith.sitofp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`shli` + +The `shli` operation shifts the integer value of the first operand to the left +by the integer value of the second operand. The second operand is interpreted as +unsigned. The low order bits are filled with zeros. If the value of the second +operand is greater or equal than the bitwidth of the first operand, then the +operation returns poison. + +This op supports `nuw`/`nsw` overflow flags which stands stand for +\"No Unsigned Wrap\" and \"No Signed Wrap\", respectively. If the `nuw` and/or +`nsw` flags are present, and an unsigned/signed overflow occurs +(respectively), the result is poison. + +# Example + +```mlir +%1 = arith.constant 5 : i8 // %1 is 0b00000101 +%2 = arith.constant 3 : i8 +%3 = arith.shli %1, %2 : i8 // %3 is 0b00101000 +%4 = arith.shli %1, %2 overflow : i8 +``` +""" +function shli( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + overflowFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(overflowFlags) && + push!(_attributes, namedattribute("overflowFlags", overflowFlags)) + + return IR.create_operation( + "arith.shli", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`shrsi` + +The `shrsi` operation shifts an integer value of the first operand to the right +by the value of the second operand. The first operand is interpreted as signed, +and the second operand is interpreter as unsigned. The high order bits in the +output are filled with copies of the most-significant bit of the shifted value +(which means that the sign of the value is preserved). If the value of the second +operand is greater or equal than bitwidth of the first operand, then the operation +returns poison. + +# Example + +```mlir +%1 = arith.constant 160 : i8 // %1 is 0b10100000 +%2 = arith.constant 3 : i8 +%3 = arith.shrsi %1, %2 : (i8, i8) -> i8 // %3 is 0b11110100 +%4 = arith.constant 96 : i8 // %4 is 0b01100000 +%5 = arith.shrsi %4, %2 : (i8, i8) -> i8 // %5 is 0b00001100 +``` +""" +function shrsi( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.shrsi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`shrui` + +The `shrui` operation shifts an integer value of the first operand to the right +by the value of the second operand. The first operand is interpreted as unsigned, +and the second operand is interpreted as unsigned. The high order bits are always +filled with zeros. If the value of the second operand is greater or equal than the +bitwidth of the first operand, then the operation returns poison. + +# Example + +```mlir +%1 = arith.constant 160 : i8 // %1 is 0b10100000 +%2 = arith.constant 3 : i8 +%3 = arith.shrui %1, %2 : (i8, i8) -> i8 // %3 is 0b00010100 +``` +""" +function shrui( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.shrui", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`subf` + +The `subf` operation takes two operands and returns one result, each of +these is required to be the same type. This type may be a floating point +scalar type, a vector whose element type is a floating point type, or a +floating point tensor. + +# Example + +```mlir +// Scalar subtraction. +%a = arith.subf %b, %c : f64 + +// SIMD vector subtraction, e.g. for Intel SSE. +%f = arith.subf %g, %h : vector<4xf32> + +// Tensor subtraction. +%x = arith.subf %y, %z : tensor<4x?xbf16> +``` + +TODO: In the distant future, this will accept optional attributes for fast +math, contraction, rounding mode, and other controls. +""" +function subf( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.subf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`subi` + +Performs N-bit subtraction on the operands. The operands are interpreted as unsigned +bitvectors. The result is represented by a bitvector containing the mathematical +value of the subtraction modulo 2^n, where `n` is the bitwidth. Because `arith` +integers use a two\'s complement representation, this operation is applicable on +both signed and unsigned integer operands. + +The `subi` operation takes two operands and returns one result, each of +these is required to be the same type. This type may be an integer scalar type, +a vector whose element type is integer, or a tensor of integers. + +This op supports `nuw`/`nsw` overflow flags which stands stand for +\"No Unsigned Wrap\" and \"No Signed Wrap\", respectively. If the `nuw` and/or +`nsw` flags are present, and an unsigned/signed overflow occurs +(respectively), the result is poison. + +# Example + +```mlir +// Scalar subtraction. +%a = arith.subi %b, %c : i64 + +// Scalar subtraction with overflow flags. +%a = arith.subi %b, %c overflow : i64 + +// SIMD vector element-wise subtraction. +%f = arith.subi %g, %h : vector<4xi32> + +// Tensor element-wise subtraction. +%x = arith.subi %y, %z : tensor<4x?xi8> +``` +""" +function subi( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + overflowFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(overflowFlags) && + push!(_attributes, namedattribute("overflowFlags", overflowFlags)) + + return IR.create_operation( + "arith.subi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`truncf` + +Truncate a floating-point value to a smaller floating-point-typed value. +The destination type must be strictly narrower than the source type. +If the value cannot be exactly represented, it is rounded using the +provided rounding mode or the default one if no rounding mode is provided. +When operating on vectors, casts elementwise. +""" +function truncf( + in::Value; out::IR.Type, roundingmode=nothing, fastmath=nothing, location=Location() +) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(roundingmode) && + push!(_attributes, namedattribute("roundingmode", roundingmode)) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "arith.truncf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`trunci` + +The integer truncation operation takes an integer input of +width M and an integer destination type of width N. The destination +bit-width must be smaller than the input bit-width (N < M). +The top-most (N - M) bits of the input are discarded. + +# Example + +```mlir + %1 = arith.constant 21 : i5 // %1 is 0b10101 + %2 = arith.trunci %1 : i5 to i4 // %2 is 0b0101 + %3 = arith.trunci %1 : i5 to i3 // %3 is 0b101 + + %5 = arith.trunci %0 : vector<2 x i32> to vector<2 x i16> +``` +""" +function trunci(in::Value; out::IR.Type, location=Location()) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arith.trunci", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`uitofp` + +Cast from a value interpreted as unsigned integer to the corresponding +floating-point value. If the value cannot be exactly represented, it is +rounded using the default rounding mode. When operating on vectors, casts +elementwise. +""" +function uitofp(in::Value; out::IR.Type, location=Location()) + _results = IR.Type[out,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arith.uitofp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`xori` + +The `xori` operation takes two operands and returns one result, each of +these is required to be the same type. This type may be an integer scalar +type, a vector whose element type is integer, or a tensor of integers. It +has no standard attributes. + +# Example + +```mlir +// Scalar integer bitwise xor. +%a = arith.xori %b, %c : i64 + +// SIMD vector element-wise bitwise integer xor. +%f = arith.xori %g, %h : vector<4xi32> + +// Tensor element-wise bitwise integer xor. +%x = arith.xori %y, %z : tensor<4x?xi8> +``` +""" +function xori( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.xori", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`select` + +The `arith.select` operation chooses one value based on a binary condition +supplied as its first operand. + +If the value of the first operand (the condition) is `1`, then the second +operand is returned, and the third operand is ignored, even if it was poison. + +If the value of the first operand (the condition) is `0`, then the third +operand is returned, and the second operand is ignored, even if it was poison. + +If the value of the first operand (the condition) is poison, then the +operation returns poison. + +The operation applies to vectors and tensors elementwise given the _shape_ +of all operands is identical. The choice is made for each element +individually based on the value at the same position as the element in the +condition operand. If an i1 is provided as the condition, the entire vector +or tensor is chosen. + +# Example + +```mlir +// Custom form of scalar selection. +%x = arith.select %cond, %true, %false : i32 + +// Generic form of the same operation. +%x = \"arith.select\"(%cond, %true, %false) : (i1, i32, i32) -> i32 + +// Element-wise vector selection. +%vx = arith.select %vcond, %vtrue, %vfalse : vector<42xi1>, vector<42xf32> + +// Full vector selection. +%vx = arith.select %cond, %vtrue, %vfalse : vector<42xf32> +``` +""" +function select( + condition::Value, + true_value::Value, + false_value::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[condition, true_value, false_value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arith.select", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +end # arith diff --git a/src/Dialects/19/ArmNeon.jl b/src/Dialects/19/ArmNeon.jl new file mode 100644 index 00000000..73294ef3 --- /dev/null +++ b/src/Dialects/19/ArmNeon.jl @@ -0,0 +1,204 @@ +module arm_neon + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`intr_smull` + +Signed Multiply Long (vector). This instruction multiplies corresponding +signed integer values in the lower or upper half of the vectors of the two +source SIMD&FP registers, places the results in a vector, and writes the +vector to the destination SIMD&FP register. + +Source: +https://developer.arm.com/architectures/instruction-sets/simd-isas/neon/intrinsics +""" +function intr_smull(a::Value, b::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_neon.intr.smull", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`_2d_sdot` + +The two input vectors `b` and `c` have a 2D shape, consisting of either 2 +or 4 rows, each row having length 4. This operation computes the pair-wise +dot-products of the rows of `b` and `c` and accumulates them with the +corresponding entry of `a`: + +``` +res[i] := a[i] + dot_product(b[i, ...], c[i, ...]) +``` +""" +function _2d_sdot(a::Value, b::Value, c::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[a, b, c] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_neon.2d.sdot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_sdot` + +Signed integer addition of dot product (vector). This instruction performs +the following operation on signed integer vectors: res = dot(b, c) + a, +where vector operands are partitioned into groups of four elements. + +Source: +https://developer.arm.com/architectures/instruction-sets/simd-isas/neon/intrinsics +""" +function intr_sdot(a::Value, b::Value, c::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[a, b, c] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_neon.intr.sdot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_smmla` + +SMMLA: Signed integer matrix multiply-accumulate. + +Signed 8-bit integer matrix multiply-accumulate. This instruction multiplies +the 2x8 matrix of signed 8-bit integer values in the first source vector by +the 8x2 matrix of signed 8-bit integer values in the second source vector. +The resulting 2x2 32-bit integer matrix product is destructively added to +the 32-bit integer matrix accumulator in the destination vector. This is +equivalent to performing an 8-way dot product per destination element. + +Source: +https://developer.arm.com/architectures/instruction-sets/intrinsics/#f:@navigationhierarchiessimdisa=[Neon]&q=smmla +""" +function intr_smmla(acc::Value, src1::Value, src2::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[acc, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_neon.intr.smmla", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ummla` + +UMMLA: Signed integer matrix multiply-accumulate. + +Unsigned 8-bit integer matrix multiply-accumulate. This instruction +multiplies the 2x8 matrix of unsigned 8-bit integer values in the first +source vector by the 8x2 matrix of unsigned 8-bit integer values in the +second source vector. The resulting 2x2 32-bit integer matrix product is +destructively added to the 32-bit integer matrix accumulator in the +destination vector. This is equivalent to performing an 8-way dot product +per destination element. + +Source: +https://developer.arm.com/architectures/instruction-sets/intrinsics/#f:@navigationhierarchiessimdisa=[Neon]&q=ummla +""" +function intr_ummla(acc::Value, src1::Value, src2::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[acc, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_neon.intr.ummla", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_usmmla` + +USMMLA: Signed integer matrix multiply-accumulate. + +Unsigned and signed 8-bit integer matrix multiply-accumulate. This +instruction multiplies the 2x8 matrix of unsigned 8-bit integer values in +the first source vector by the 8x2 matrix of signed 8-bit integer values in +the second source vector. The resulting 2x2 32-bit integer matrix product is +destructively added to the 32-bit integer matrix accumulator in the +destination vector. This is equivalent to performing an 8-way dot product + per destination element. + + +Source: +https://developer.arm.com/architectures/instruction-sets/intrinsics/#f:@navigationhierarchiessimdisa=[Neon]&q=usmmla +""" +function intr_usmmla( + acc::Value, src1::Value, src2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[acc, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_neon.intr.usmmla", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # arm_neon diff --git a/src/Dialects/19/ArmSME.jl b/src/Dialects/19/ArmSME.jl new file mode 100644 index 00000000..10b3b851 --- /dev/null +++ b/src/Dialects/19/ArmSME.jl @@ -0,0 +1,2937 @@ +module arm_sme + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`copy_tile` + +Copies an SME \"virtual tile\" value to a new SSA value. This operation is +primarily intended to be used to normalize the IR prior to tile allocation. + +# Example + +```mlir +%copy = arm_sme.copy_tile %tile : vector<[4]x[4]xf32> +``` +""" +function copy_tile(tile::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[tile,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "arm_sme.copy_tile", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`fmopa_2way` + +This operation represents a sum of 2 widened outer products. It takes 2 1-D +scalable vectors as input and a 2-D scalable vector (ZA tile) as output. + +For example (fp16 to fp32): + +```mlir +%result = arm_sme.fmopa_2way %lhs, %rhs : + vector<[8]xf16>, vector<[8]xf16> into vector<[4]x[4]xf32> +``` + +The `lhs` encodes a matrix of shape SVLSx2 and the `rhs` a matrix of +2xSVLS, where SVLS (spec [1], section B2.1) is the number of 32-bit +elements in a vector of SVL bits. To illustrate, below is a breakdown of +this operation for fp16 to fp32, SVL=128 (i.e., vscale=1): + +``` + LHS RHS + [A0 A1 A2 A3 A4 A5 A6 A7] [B0 B1 B2 B3 B4 B5 B6 B7] + +---------------------------------------------------------------------------- + + implicit layout + + [A0 A1] | + [A2 A3] | [B0 B2 B4 B6] + [A4 A5] | [B1 B3 B5 B7] + [A6 A7] | + +---------------------------------------------------------------------------- + + 2 outer products + + Acol0 ⊗ Brow0 | Acol1 ⊗ Brow1 + ------------- | ------------- + | + [B0 B2 B4 B6] | [B1 B3 B5 B7] + | + [A0 [A0B0 A0B2 A0B4 A0B6] | [A1 [A1B1 A1B3 A1B5 A1B7] + A2 [A2B0 A2B2 A2B4 A2B6] | A3 [A3B1 A3B3 A3B5 A3B7] + A4 [A4B0 A4B2 A4B4 A4B6] | A5 [A5B1 A5B3 A5B5 A5B7] + A6] [A6B0 A6B2 A6B4 A6B6] | A7] [A7B1 A7B3 A7B5 A7B7] + | + +---------------------------------------------------------------------------- + + sum of 2 outer products + + Acol0 ⊗ Brow0 + Acol1 ⊗ Brow1 + + [A0B0 + A1B1 A0B2 + A1B3 A0B4 + A1B5 A0B6 + A1B7] + [A2B0 + A3B1 A2B2 + A3B3 A2B4 + A3B5 A2B6 + A3B7] + [A4B0 + A5B1 A4B2 + A5B3 A4B4 + A5B5 A4B6 + A5B7] + [A6B0 + A7B1 A6B2 + A7B3 A6B4 + A7B5 A6B6 + A7B7] + +---------------------------------------------------------------------------- +``` + +This operation enables the folding of 2 outer products chained via the +accumulator into a single outer product. + +For example: + +```mlir +%a0_ext = arith.extf %a0 : vector<[4]xf16> to vector<[4]xf32> +%b0_ext = arith.extf %b0 : vector<[4]xf16> to vector<[4]xf32> +%a1_ext = arith.extf %a1 : vector<[4]xf16> to vector<[4]xf32> +%b1_ext = arith.extf %b1 : vector<[4]xf16> to vector<[4]xf32> + +%0 = arm_sme.outerproduct %a0_ext, %b0_ext : vector<[4]xf32>, vector<[4]xf32> +%1 = arm_sme.outerproduct %a1_ext, %b1_ext acc(%0) : vector<[4]xf32>, vector<[4]xf32> +``` + +The 2 outer products in the example above can be fused into a single outer +product as follows: + + ```mlir +%a_packed = \"llvm.intr.experimental.vector.interleave2\"(%a0, %a1) : (vector<[4]xf16>, vector<[4]xf16>) -> vector<[8]xf16> +%b_packed = \"llvm.intr.experimental.vector.interleave2\"(%b0, %b1) : (vector<[4]xf16>, vector<[4]xf16>) -> vector<[8]xf16> +%0 = arm_sme.fmopa_2way %a_packed, %b_packed : vector<[8]xf16>, vector<[8]xf16> into vector<[4]x[4]xf32> + ``` + +This is implemented in the `-arm-sme-outer-product-fusion` pass. + +# Example FP16 to FP32 +```mlir +%result = arm_sme.fmopa_2way \$lhs, \$rhs : vector<[8]xf16>, vector<[8]xf16> into vector<[4]x[4]xf32> +``` + +# Example BF16 to FP32 +```mlir +%result = arm_sme.fmopa_2way \$lhs, \$rhs : vector<[8]xbf16>, vector<[8]xbf16> into vector<[4]x[4]xf32> +``` + +| Spec | Features | +| ---- | -------- | +| [FMOPA (widening, 2-way, FP16 to FP32)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/FMOPA--widening--2-way--FP16-to-FP32---Half-precision-floating-point-sum-of-outer-products-and-accumulate-) | +sme | +| [BFMOPA (widening, 2-way, BF16 to FP32)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/BFMOPA--widening---BFloat16-sum-of-outer-products-and-accumulate-) | +sme | + +[1] https://developer.arm.com/documentation/ddi0616 +""" +function fmopa_2way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.fmopa_2way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fmops_2way` + +Equivalent to `fmopa_2way` but outer products are subtracted from +destination `result`. + +# Example FP16 to FP32 +```mlir +%result = arm_sme.fmops_2way \$lhs, \$rhs : vector<[8]xf16>, vector<[8]xf16> into vector<[4]x[4]xf32> +``` + +# Example BF16 to FP32 +```mlir +%result = arm_sme.fmops_2way \$lhs, \$rhs : vector<[8]xbf16>, vector<[8]xbf16> into vector<[4]x[4]xf32> +``` + +Refer to +[fmopa_2way](#arm_smefmopa_2way-arm_smefmopa2wayop) for a detailed +description of 2-way outer products. + +| Spec | Features | +| ---- | -------- | +| [FMOPS (widening, 2-way, FP16 to FP32)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/FMOPS--widening---Half-precision-floating-point-sum-of-outer-products-and-subtract-) | +sme | +| [BFMOPS (widening, 2-way, BF16 to FP32)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/BMOPS--Bitwise-exclusive-NOR-population-count-outer-product-and-subtract-) | +sme | +""" +function fmops_2way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.fmops_2way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_tile` + +Creates a new SME \"virtual tile\" value within a function. The contents of +the tile returned from this operation are undefined. + +Example 1: + +```mlir +// Create an 8-bit element \"virtual tile\" value: +%za0_b = arm_sme.get_tile: vector<[16]x[16]xi8> +``` + +Example 2: + +```mlir +// Create two 16-bit element \"virtual tiles\" values: +%za0_h = arm_sme.get_tile : vector<[8]x[8]xi16> +%za1_h = arm_sme.get_tile : vector<[8]x[8]xi16> +``` + +Example 3: +```mlir +// Create an 128-bit element \"virtual tile\" value: +%za0_q = arm_sme.get_tile : vector<[1]x[1]xi128> +``` +""" +function get_tile(; tile::IR.Type, location=Location()) + _results = IR.Type[tile,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sme.get_tile", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`load_tile_slice` + +Loads a 1D tile slice from memory into a 2D SME \"virtual tile\". The tile +slice is defined by the dimension of the 2D scalable vector type pointed by +the index. A tile slice index describes where in the input tile the tile +slice is loaded to. An optional tile slice layout attribute specifies +whether the tile slice being loaded at the given index is horizontal +(default) or vertical. The updated tile is returned as the result. + +The slice of memory read is defined by a base and indices and must be +contiguous. The memref must be either rank 1 or rank 2, have dynamic +dimensions since the operation is scalable, and the element type must be a +scalar that matches the element type of the result. + +The provided `mask` is used to specify which elements of the tile slice +will be loaded. + +Example 1: Load a vector<[16]xi8> tile slice from memory into tile horizontally (default) at given index. +```mlir +%tile_update = arm_sme.load_tile_slice %base[%c0], %mask, %tile, %tile_slice_index : memref, vector<[16]xi1>, vector<[16]x[16]xi8> +``` + +Example 2: Load a vector<[4]xf32> tile slice from memory into tile vertically at given index. +```mlir +%tile_update = arm_sme.load_tile_slice %base[%c0], %mask, %tile, %tile_slice_index layout : memref, vector<[4]xi1>, vector<[4]x[4]xf32> +``` + +Example 3: Load a vector<[1]xi128> tile slice from memory into tile vertically at given index. +```mlir +%tile_update = arm_sme.load_tile_slice %base[%c0], %mask, %tile, %tile_slice_index layout : memref, vector<[1]xi1>, vector<[1]x[1]xi128> +``` +""" +function load_tile_slice( + base::Value, + mask::Value, + tile::Value, + indices::Vector{Value}, + tile_slice_index::Value; + result=nothing::Union{Nothing,IR.Type}, + layout=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[base, mask, tile, indices..., tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(layout) && push!(_attributes, namedattribute("layout", layout)) + + return IR.create_operation( + "arm_sme.load_tile_slice", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`move_tile_slice_to_vector` + +The tile slice to vector operation extracts a 1-D scalable slice from a 2-D +scalable tile at the given index. A tile slice is a 1-D vector of +horizontally or vertically contiguous elements within a ZA tile. + +An optional tile slice layout attribute specifies whether the tile slice is +horizontal (default) or vertical. + +Example 1: Extract `vector<[16]xi8>` from tile horizontally at the given index. +```mlir +%slice = arm_sme.move_tile_slice_to_vector %tile[%tile_slice_index] : vector<[16]xi8> from vector<[16]x[16]xi8> +``` + +Example 2: Extract `vector<[2]xf64>` from tile vertically at the given index. +```mlir +%slice = arm_sme.move_tile_slice_to_vector %tile[%tile_slice_index] layout : vector<[2]xf64> from vector<[2]x[2]xf64> +``` +""" +function move_tile_slice_to_vector( + tile::Value, + tile_slice_index::Value; + result=nothing::Union{Nothing,IR.Type}, + layout=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[tile, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(layout) && push!(_attributes, namedattribute("layout", layout)) + + return IR.create_operation( + "arm_sme.move_tile_slice_to_vector", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`move_vector_to_tile_slice` + +The vector to tile slice operation moves a 1-D scalable vector to a slice +of a 2-D scalable vector tile at the given index. The type of the 1-D +scalable vector to be moved must match the type of the tile slice. A tile +slice is a 1-D vector of horizontally or vertically contiguous elements +within a ZA tile. The updated tile is returned as the result. + +An optional tile slice layout attribute specifies whether the tile slice is +horizontal (default) or vertical. + +Example 1: Move a vector<[16]xi8> into tile horizontally (default) at given index. +```mlir +%tile_update = arm_sme.move_vector_to_tile_slice %vector, %tile, %tile_slice_index : vector<[16]xi8> into vector<[16]x[16]xi8> +``` + +Example 2: Move a vector<[2]xf64> into tile vertically at given index. +```mlir +%tile_update = arm_sme.move_vector_to_tile_slice %vector, %tile, %tile_slice_index layout : vector<[2]xf64> into vector<[2]x[2]xf64> +``` +""" +function move_vector_to_tile_slice( + vector::Value, + tile::Value, + tile_slice_index::Value; + result=nothing::Union{Nothing,IR.Type}, + layout=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vector, tile, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(layout) && push!(_attributes, namedattribute("layout", layout)) + + return IR.create_operation( + "arm_sme.move_vector_to_tile_slice", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`outerproduct` + +This operation represents an outer product that fits within an SME tile. +All operands must be SVE vectors and the result a SME tile. Unlike +`vector.outerproduct` masking is on the operands (rather than the result), +which mirrors the SME instructions. + +Example 1: Unmasked outerproduct (without accumulator) +```mlir +// Not specifying an accumulator implicitly zeros the destination tile. +%result = arm_sme.outerproduct \$lhs, \$rhs : vector<[4]xf32>, vector<[4]xf32> +``` + +Example 2: Unmasked outerproduct (with accumulator) +```mlir +%result = arm_sme.outerproduct \$lhs, \$rhs acc(\$accumulator) + : vector<[4]xf32>, vector<[4]xf32> +``` + +Example 3: Masked outerproduct +```mlir +%result = arm_sme.outerproduct \$lhs, \$rhs masks(\$lhsMask, \$rhsMask) + : vector<[4]xf32>, vector<[4]xf32> +``` + +Example 4: Masked outerproduct (with accumulator) +```mlir +%result = arm_sme.outerproduct \$lhs, \$rhs acc(\$accumulator) masks(\$lhsMask, \$rhsMask) + : vector<[4]xf32>, vector<[4]xf32> +``` +""" +function outerproduct( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result=nothing::Union{Nothing,IR.Type}, + kind=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + !isnothing(result) && push!(_results, result) + !isnothing(kind) && push!(_attributes, namedattribute("kind", kind)) + + return IR.create_operation( + "arm_sme.outerproduct", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`smopa_2way` + +# Example +```mlir +%result = arm_sme.smopa_2way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[4]x[4]xi32> +``` + +Refer to +[fmopa_2way](#arm_smefmopa_2way-arm_smefmopa2wayop) for a detailed +description of 2-way outer products. + +| Spec | Features | +| ---- | -------- | +| [SMOPA (2-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/SMOPA--2-way---Signed-integer-sum-of-outer-products-and-accumulate-) | +sme2 | +""" +function smopa_2way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.smopa_2way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`smopa_4way` + +This operation represents a sum of 4 widened outer products. It takes 2 1-D +scalable vectors as input and a 2-D scalable vector (ZA tile) as output. + +For example (i8 to i32): + +```mlir +%result = arm_sme.smopa_4way \$lhs, \$rhs : + vector<[16]xi8>, vector<[16]xi8> into vector<[4]x[4]xi32> +``` + +The `lhs` encodes a matrix of shape SVLSx4 and the `rhs` a matrix of +4xSVLS, where SVLS (spec [1], section B2.1) is the number of 32-bit +elements in a vector of SVL bits. To illustrate, below is a breakdown of +this operation for i8 to i32, SVL=128 (i.e., vscale=1): + +``` + LHS + [A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A15 A14 A15] + + RHS + [B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15] + +---------------------------------------------------------------------------- + + implicit layout + + [A0 A1 A2 A3] | [B0 B4 B8 B12] + [A4 A5 A6 A7] | [B1 B5 B9 B13] + [A8 A9 A10 A11] | [B2 B6 B10 B14] + [A12 A13 A14 A15] | [B3 B7 B11 B15] + +---------------------------------------------------------------------------- + + 4 outer products + + Acol0 ⊗ Brow0 | Acol1 ⊗ Brow1 + ------------- | ------------- + | + [B0 B4 B8 B12] | [B1 B5 B9 B13] + | + [A0 [ A0B0 A0B4 A0B8 A0B12] | [A1 [ A1B1 A1B5 A1B9 A1B13] + A4 [ A4B0 A4B4 A4B8 A4B12] | A5 [ A5B1 A5B5 A5B9 A5B13] + A8 [ A8B0 A8B4 A8B8 A8B12] | A9 [ A9B1 A9B5 A9B9 A9B13] + A12] [A12B0 A12B4 A12B8 A12B12] | A13] [A13B1 A13B5 A13B9 A13B13] + | + Acol2 ⊗ Brow2 | Acol3 ⊗ Brow3 + ------------- | ------------- + | + [B2, B6, B10, B14] | [B3 B7 B11 B15] + | + [A2 [ A2B2 A2B6 A2B10 A2B14] | [A3 [ A3B3 A3B7 A3B11 A3B15] + A6 [ A6B2 A6B6 A6B10 A6B14] | A7 [ A7B3 A7B7 A7B11 A7B15] + A10 [A10B2 A10B6 A10B10 A10B14] | A11 [A11B3 A11B7 A11B11 A11B15] + A14] [A14B2 A14B6 A14B10 A14B14] | A15] [A15B3 A15B7 A15B11 A15B15] + | + +---------------------------------------------------------------------------- + + sum of 4 outer products + + Acol0 ⊗ Brow0 + Acol1 ⊗ Brow1 + Acol2 ⊗ Brow2 + Acol3 ⊗ Brow3 + + [ A0B0 + A1B1 + A2B2 + A3B3 ... ... A0B12 + A1B13 + A2B14 + A3B15] + [ A4B0 + A5B1 + A6B2 + A7B3 ... ... A4B12 + A5B13 + A6B14 + A7B15] + [ A8B0 + A9B1 + A10B2 + A11B3 ... ... A8B12 + A9B13 + A10B14 + A11B15] + [A12B0 + A13B1 + A14B2 + A15B3 ... ... A12B12 + A13B13 + A14B14 + A15B15] + +---------------------------------------------------------------------------- +``` + +This operation enables the folding of 4 outer products chained via the +accumulator into a single outer product. + +For example: + +```mlir +%a0_ext = arith.extsi %a0 : vector<[4]xi8> to vector<[4]xi32> +%b0_ext = arith.extsi %b0 : vector<[4]xi8> to vector<[4]xi32> + +%a1_ext = arith.extsi %a1 : vector<[4]xi8> to vector<[4]xi32> +%b1_ext = arith.extsi %b1 : vector<[4]xi8> to vector<[4]xi32> + +%a2_ext = arith.extsi %a2 : vector<[4]xi8> to vector<[4]xi32> +%b2_ext = arith.extsi %b2 : vector<[4]xi8> to vector<[4]xi32> + +%a3_ext = arith.extsi %a3 : vector<[4]xi8> to vector<[4]xi32> +%b3_ext = arith.extsi %b3 : vector<[4]xi8> to vector<[4]xi32> + +%0 = arm_sme.outerproduct %a0_ext, %b0_ext : vector<[4]xi32>, vector<[4]xi32> +%1 = arm_sme.outerproduct %a1_ext, %b1_ext acc(%0) : vector<[4]xi32>, vector<[4]xi32> +%2 = arm_sme.outerproduct %a2_ext, %b2_ext acc(%1) : vector<[4]xi32>, vector<[4]xi32> +%3 = arm_sme.outerproduct %a3_ext, %b3_ext acc(%2) : vector<[4]xi32>, vector<[4]xi32> +``` + +The 4 outer products in the example above can be fused into a single outer +product as follows: + +```mlir +%lhs0 = \"llvm.intr.experimental.vector.interleave2\"(%a0, %a2) : (vector<[4]xi8>, vector<[4]xi8>) -> vector<[8]xi8> +%lhs1 = \"llvm.intr.experimental.vector.interleave2\"(%a1, %a3) : (vector<[4]xi8>, vector<[4]xi8>) -> vector<[8]xi8> +%lhs = \"llvm.intr.experimental.vector.interleave2\"(%lhs0, %lhs1) : (vector<[8]xi8>, vector<[8]xi8>) -> vector<[16]xi8> + +%rhs0 = \"llvm.intr.experimental.vector.interleave2\"(%b0, %b2) : (vector<[4]xi8>, vector<[4]xi8>) -> vector<[8]xi8> +%rhs1 = \"llvm.intr.experimental.vector.interleave2\"(%b1, %b3) : (vector<[4]xi8>, vector<[4]xi8>) -> vector<[8]xi8> +%rhs = \"llvm.intr.experimental.vector.interleave2\"(%rhs0, %rhs1) : (vector<[8]xi8>, vector<[8]xi8>) -> vector<[16]xi8> + +%0 = arm_sme.smopa_4way %lhs, %rhs : vector<[16]xi8>, vector<[16]xi8> into vector<[4]x[4]xi32> +``` + +This is implemented in the `-arm-sme-outer-product-fusion` pass. + +# Example I8 to I32 +```mlir +%result = arm_sme.smopa_4way \$lhs, \$rhs : vector<[16]xi8>, vector<[16]xi8> into vector<[4]x[4]xi32> +``` + +# Example I16 to I64 +```mlir +%result = arm_sme.smopa_4way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[2]x[2]xi64> +``` + +| Spec | Features | +| ---- | -------- | +| [SMOPA (4-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/SMOPA--4-way---Signed-integer-sum-of-outer-products-and-accumulate-) | +sme (32-bit), +sme-i16i64 (64-bit)| +""" +function smopa_4way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.smopa_4way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`smops_2way` + +# Example +```mlir +%result = arm_sme.smops_2way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[4]x[4]xi32> +``` + +Refer to +[fmopa_2way](#arm_smefmopa_2way-arm_smefmopa2wayop) for a detailed +description of 2-way outer products. + +| Spec | Features | +| ---- | -------- | +| [SMOPS (2-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/SMOPS--2-way---Signed-integer-sum-of-outer-products-and-subtract-) | +sme2 | +""" +function smops_2way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.smops_2way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`smops_4way` + +Equivalent to `smopa_4way` but outer products are subtracted from +destination `result`. + +# Example I8 to I32 +```mlir +%result = arm_sme.smops_4way \$lhs, \$rhs : vector<[16]xi8>, vector<[16]xi8> into vector<[4]x[4]xi32> +``` + +# Example I16 to I64 +```mlir +%result = arm_sme.smops_4way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[2]x[2]xi64> +``` + +Refer to [smopa_4way](#arm_smesmopa_4way-arm_smesmopa4wayop) for a +detailed description of 4-way outer products. + +| Spec | Features | +| ---- | -------- | +| [SMOPS (4-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/SMOPS--4-way---Signed-integer-sum-of-outer-products-and-subtract-) | +sme (32-bit), +sme-i16i64 (64-bit)| +""" +function smops_4way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.smops_4way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`store_tile_slice` + +Stores a 1D tile slice from a 2D SME \"virtual tile\" into memory. The tile +slice is defined by the dimension of the 2D scalable vector type pointed by +the index. A tile slice index describes where in the input tile the tile +slice is stored from. An optional tile slice layout attribute specifies +whether the tile slice being stored from the given index is horizontal +(default) or vertical. + +The slice of memory written is defined by a base and indices and must be +contiguous. The memref must be either rank 1 or rank 2, have dynamic +dimensions since the operation is scalable, and the element type must be a +scalar that matches the element type of the input tile. + +The provided `mask` is used to specify which elements of the tile slice +will be stored. + +Example 1: Store vector<[16]xi8> horizontal (default) tile slice from tile at given index to memory. +```mlir +arm_sme.store_tile_slice %tile, %tile_slice_index, %mask, %base[%c0] : vector<[16]x[16]xi8>, vector<[16]xi1>, memref +``` + +Example 2: Store vector<[4]xf32> vertical tile slice from tile at given index to memory. +```mlir +arm_sme.store_tile_slice %tile, %tile_slice_index, %mask, %base[%c0] layout : vector<[4]x[4]xf32>, vector<[4]xi1>, memref +``` + +Example 3: Store a vector<[1]xi128> vertical tile slice from tile at given index to memory. +```mlir +arm_sme.store_tile_slice %tile, %tile_slice_index, %mask, %base[%c0] layout : vector<[1]x[1]xi128>, vector<[1]xi1>, memref +``` +""" +function store_tile_slice( + tile::Value, + tile_slice_index::Value, + mask::Value, + base::Value, + indices::Vector{Value}; + layout=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[tile, tile_slice_index, mask, base, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(layout) && push!(_attributes, namedattribute("layout", layout)) + + return IR.create_operation( + "arm_sme.store_tile_slice", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`streaming_vl` + +This operation returns the streaming vector length (SVL) for a given type +size. Unlike `vector.vscale` the value returned is invariant to the +streaming mode. + +# Example +```mlir +// Streaming vector length in: +// - bytes (8-bit, SVL.B) +%svl_b = arm_sme.streaming_vl +// - half words (16-bit, SVL.H) +%svl_h = arm_sme.streaming_vl +// - words (32-bit, SVL.W) +%svl_w = arm_sme.streaming_vl +// - double words (64-bit, SVL.D) +%svl_d = arm_sme.streaming_vl +``` +""" +function streaming_vl(; + result_0=nothing::Union{Nothing,IR.Type}, type_size, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("type_size", type_size),] + !isnothing(result_0) && push!(_results, result_0) + + return IR.create_operation( + "arm_sme.streaming_vl", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sumopa_4way` + +# Example I8 to I32 +```mlir +%result = arm_sme.sumopa_4way \$lhs, \$rhs : vector<[16]xi8>, vector<[16]xi8> into vector<[4]x[4]xi32> +``` + +# Example I16 to I64 +```mlir +%result = arm_sme.sumopa_4way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[2]x[2]xi64> +``` + +Refer to [smopa_4way](#arm_smesmopa_4way-arm_smesmopa4wayop) for a +detailed description of 4-way outer products. + +| Spec | Features | +| ---- | -------- | +| [SUMOPA (4-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/SUMOPA--Signed-by-unsigned-integer-sum-of-outer-products-and-accumulate-) | +sme (32-bit), +sme-i16i64 (64-bit)| +""" +function sumopa_4way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.sumopa_4way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sumops_4way` + +# Example I8 to I32 +```mlir +%result = arm_sme.sumops_4way \$lhs, \$rhs : vector<[16]xi8>, vector<[16]xi8> into vector<[4]x[4]xi32> +``` + +# Example I16 to I64 +```mlir +%result = arm_sme.sumops_4way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[2]x[2]xi64> +``` + +Refer to [smopa_4way](#arm_smesmopa_4way-arm_smesmopa4wayop) for a +detailed description of 4-way outer products. + +| Spec | Features | +| ---- | -------- | +| [SUMOPS (4-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/SUMOPS--Signed-by-unsigned-integer-sum-of-outer-products-and-subtract-) | +sme (32-bit), +sme-i16i64 (64-bit)| +""" +function sumops_4way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.sumops_4way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tile_load` + +Loads a 2D SME \"virtual tile\" from memory defined by a base and indices, +with the shape defined by the 2D scalable vector type of the result tile. +An optional tile slice layout attribute specifies whether the slices of the +tile being loaded are horizontal (default) or vertical. The slice of memory +must be contiguous. The memref must be either rank 1 or rank 2 with dynamic +dimensions, since the operation is scalable, and the element type must be a +scalar that matches the element type of the result. + +An optional SSA value `padding` of the same elemental type as the MemRef is +provided to specify a fallback value in the case of masking. + +An optional SSA value `mask` may be specified to mask out elements read +from the MemRef. The `mask` type is an `i1` vector with a shape that +matches how elements are read from the MemRef. Elements whose corresponding +mask element is `0` are masked out and replaced with `padding`. + +If either `padding` or `mask` are specified, both must be specified. + +Example 1: Load an 8-bit element ZA tile with horizontal layout (default) from memory (ZA0.B). +```mlir +%tile = arm_sme.tile_load %base[%c0, %c0] : memref, vector<[16]x[16]xi8> +``` + +Example 2: Load a FP 32-bit element ZA tile with vertical layout from memory. +```mlir +%tile = arm_sme.tile_load %base[%c0, %c0] layout : memref, vector<[4]x[4]xf32> +``` + +Example 3: Load a 128-bit element ZA tile with horizontal layout (default) from memory. +```mlir +%tile = arm_sme.tile_load %base[%c0, %c0] layout : memref, vector<[1]x[1]xi128> +``` + +Example 4: Masked load of int 32-bit element ZA tile with horizontal layout (default) from memory. +```mlir +%tile = arm_sme.tile_load %base[%c0, %c0], %pad, %mask : memref, vector<[4]x[4]xf32> +``` +""" +function tile_load( + base::Value, + indices::Vector{Value}, + padding=nothing::Union{Nothing,Value}; + mask=nothing::Union{Nothing,Value}, + result::IR.Type, + layout=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[base, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(padding) && push!(_operands, padding) + !isnothing(mask) && push!(_operands, mask) + push!( + _attributes, + operandsegmentsizes([ + 1, length(indices), isnothing(padding) ? 0 : 1, isnothing(mask) ? 0 : 1 + ]), + ) + !isnothing(layout) && push!(_attributes, namedattribute("layout", layout)) + + return IR.create_operation( + "arm_sme.tile_load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tile_store` + +Stores a 2D SME \"virtual tile\" to memory defined by a base and indices, +with the shape defined by the 2D scalable vector type of the tile being +stored. An optional tile slice layout attribute specifies whether the +slices of the tile being stored are horizontal (default) or vertical. The +slice of memory must be contiguous. The memref must be either rank 1 or +rank 2 with dynamic dimensions, since the operation is scalable, and the +element type must be a scalar that matches the element type of the result. + +An optional `mask` may be provided, the shape of which corresponds to the +`tile`, and selects which elements of the tile will be stored. + +Example 1: Store an 8-bit element ZA tile with horizontal (default) layout to memory (ZA0.B). +```mlir +arm_sme.tile_store %tile, %base[%c0, %c0] : vector<[16]x[16]xi8>, memref +``` + +Example 2: Store a FP 32-bit element ZA tile with vertical layout to memory. +```mlir +arm_sme.tile_store %tile, %base[%c0, %c0] layout : vector<[4]x[4]xf32>, memref +``` + +Example 3: Store a 128-bit element ZA tile with horizontal (default) layout to memory. +```mlir +arm_sme.tile_store %tile, %base[%c0, %c0] layout : vector<[1]x[1]xi128>, memref +``` + +Example 4: Masked store a int 32-bit element ZA tile with vertical layout to memory. +```mlir +arm_sme.tile_store %tile, %base[%c0, %c0], %mask layout : vector<[4]x[4]xf32>, memref +``` +""" +function tile_store( + valueToStore::Value, + base::Value, + indices::Vector{Value}, + mask=nothing::Union{Nothing,Value}; + layout=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[valueToStore, base, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(mask) && push!(_operands, mask) + push!( + _attributes, operandsegmentsizes([1, 1, length(indices), isnothing(mask) ? 0 : 1]) + ) + !isnothing(layout) && push!(_attributes, namedattribute("layout", layout)) + + return IR.create_operation( + "arm_sme.tile_store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`umopa_2way` + +# Example +```mlir +%result = arm_sme.umopa_2way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[4]x[4]xi32> +``` + +Refer to +[fmopa_2way](#arm_smefmopa_2way-arm_smefmopa2wayop) for a detailed +description of 2-way outer products. + +| Spec | Features | +| ---- | -------- | +| [UMOPA (2-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/UMOPA--2-way---Unsigned-integer-sum-of-outer-products-and-accumulate-) | +sme2 | +""" +function umopa_2way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.umopa_2way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`umopa_4way` + +# Example I8 to I32 +```mlir +%result = arm_sme.umopa_4way \$lhs, \$rhs : vector<[16]xi8>, vector<[16]xi8> into vector<[4]x[4]xi32> +``` + +# Example I16 to I64 +```mlir +%result = arm_sme.umopa_4way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[2]x[2]xi64> +``` + +Refer to [smopa_4way](#arm_smesmopa_4way-arm_smesmopa4wayop) for a +detailed description of 4-way outer products. + +| Spec | Features | +| ---- | -------- | +| [UMOPA (4-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/UMOPA--4-way---Unsigned-integer-sum-of-outer-products-and-accumulate-) | +sme (32-bit), +sme-i16i64 (64-bit)| +""" +function umopa_4way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.umopa_4way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`umops_2way` + +# Example +```mlir +%result = arm_sme.umops_2way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[4]x[4]xi32> +``` + +Refer to +[fmopa_2way](#arm_smefmopa_2way-arm_smefmopa2wayop) for a detailed +description of 2-way outer products. + +| Spec | Features | +| ---- | -------- | +| [UMOPS (2-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/UMOPS--2-way---Unsigned-integer-sum-of-outer-products-and-subtract-) | +sme2 | +""" +function umops_2way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.umops_2way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`umops_4way` + +# Example I8 to I32 +```mlir +%result = arm_sme.umops_4way \$lhs, \$rhs : vector<[16]xi8>, vector<[16]xi8> into vector<[4]x[4]xi32> +``` + +# Example I16 to I64 +```mlir +%result = arm_sme.umops_4way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[2]x[2]xi64> +``` + +Refer to [smopa_4way](#arm_smesmopa_4way-arm_smesmopa4wayop) for a +detailed description of 4-way outer products. + +| Spec | Features | +| ---- | -------- | +| [UMOPS (4-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/UMOPS--4-way---Unsigned-integer-sum-of-outer-products-and-subtract-) | +sme (32-bit), +sme-i16i64 (64-bit)| +""" +function umops_4way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.umops_4way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`usmopa_4way` + +# Example I8 to I32 +```mlir +%result = arm_sme.usmopa_4way \$lhs, \$rhs : vector<[16]xi8>, vector<[16]xi8> into vector<[4]x[4]xi32> +``` + +# Example I16 to I64 +```mlir +%result = arm_sme.usmopa_4way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[2]x[2]xi64> +``` + +Refer to [smopa_4way](#arm_smesmopa_4way-arm_smesmopa4wayop) for a +detailed description of 4-way outer products. + +| Spec | Features | +| ---- | -------- | +| [USMOPA (4-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/USMOPA--Unsigned-by-signed-integer-sum-of-outer-products-and-accumulate-) | +sme (32-bit), +sme-i16i64 (64-bit)| +""" +function usmopa_4way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.usmopa_4way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`usmops_4way` + +# Example I8 to I32 +```mlir +%result = arm_sme.usmops_4way \$lhs, \$rhs : vector<[16]xi8>, vector<[16]xi8> into vector<[4]x[4]xi32> +``` + +# Example I16 to I64 +```mlir +%result = arm_sme.usmops_4way \$lhs, \$rhs : vector<[8]xi16>, vector<[8]xi16> into vector<[2]x[2]xi64> +``` + +Refer to [smopa_4way](#arm_smesmopa_4way-arm_smesmopa4wayop) for a +detailed description of 4-way outer products. + +| Spec | Features | +| ---- | -------- | +| [USMOPS (4-way)](https://developer.arm.com/documentation/ddi0602/2023-09/SME-Instructions/USMOPS--Unsigned-by-signed-integer-sum-of-outer-products-and-subtract-) | +sme (32-bit), +sme-i16i64 (64-bit)| +""" +function usmops_4way( + lhs::Value, + rhs::Value, + lhsMask=nothing::Union{Nothing,Value}; + rhsMask=nothing::Union{Nothing,Value}, + acc=nothing::Union{Nothing,Value}, + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lhsMask) && push!(_operands, lhsMask) + !isnothing(rhsMask) && push!(_operands, rhsMask) + !isnothing(acc) && push!(_operands, acc) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + isnothing(lhsMask) ? 0 : 1, + isnothing(rhsMask) ? 0 : 1, + isnothing(acc) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "arm_sme.usmops_4way", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`zero` + +Creates a new SME \"virtual tile\" value within a function. The contents of +the tile returned from this operation are zero-initialized. + +Example 1: Zero an 8-bit element ZA tile. + +```mlir +%0 = arm_sme.zero : vector<[16]x[16]xi8> +``` + +Example 2: Zero a 64-bit element ZA tile. + +```mlir +%0 = arm_sme.zero : vector<[2]x[2]xi64> +``` +""" +function zero(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sme.zero", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`intr_cntsb` + +""" +function intr_cntsb(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sme.intr.cntsb", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_cntsd` + +""" +function intr_cntsd(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sme.intr.cntsd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_cntsh` + +""" +function intr_cntsh(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sme.intr.cntsh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_cntsw` + +""" +function intr_cntsw(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sme.intr.cntsw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ld1b_horiz` + +""" +function intr_ld1b_horiz( + predicate::Value, + load_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, load_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.ld1b.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ld1b_vert` + +""" +function intr_ld1b_vert( + predicate::Value, + load_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, load_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.ld1b.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ld1d_horiz` + +""" +function intr_ld1d_horiz( + predicate::Value, + load_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, load_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.ld1d.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ld1d_vert` + +""" +function intr_ld1d_vert( + predicate::Value, + load_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, load_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.ld1d.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ld1h_horiz` + +""" +function intr_ld1h_horiz( + predicate::Value, + load_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, load_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.ld1h.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ld1h_vert` + +""" +function intr_ld1h_vert( + predicate::Value, + load_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, load_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.ld1h.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ld1q_horiz` + +""" +function intr_ld1q_horiz( + predicate::Value, + load_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, load_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.ld1q.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ld1q_vert` + +""" +function intr_ld1q_vert( + predicate::Value, + load_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, load_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.ld1q.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ld1w_horiz` + +""" +function intr_ld1w_horiz( + predicate::Value, + load_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, load_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.ld1w.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ld1w_vert` + +""" +function intr_ld1w_vert( + predicate::Value, + load_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, load_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.ld1w.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_mopa` + +""" +function intr_mopa( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.mopa", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_mopa_wide` + +""" +function intr_mopa_wide( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.mopa.wide", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_mops` + +""" +function intr_mops( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.mops", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_mops_wide` + +""" +function intr_mops_wide( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.mops.wide", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_read_horiz` + +""" +function intr_read_horiz( + vector::Value, + predicate::Value, + tile_slice_index::Value; + res::IR.Type, + tile_id, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[vector, predicate, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.read.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_read_vert` + +""" +function intr_read_vert( + vector::Value, + predicate::Value, + tile_slice_index::Value; + res::IR.Type, + tile_id, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[vector, predicate, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.read.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_smopa_wide` + +""" +function intr_smopa_wide( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.smopa.wide", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_smopa_za32` + +""" +function intr_smopa_za32( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.smopa.za32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_smops_wide` + +""" +function intr_smops_wide( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.smops.wide", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_smops_za32` + +""" +function intr_smops_za32( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.smops.za32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_st1b_horiz` + +""" +function intr_st1b_horiz( + predicate::Value, + store_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, store_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.st1b.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_st1b_vert` + +""" +function intr_st1b_vert( + predicate::Value, + store_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, store_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.st1b.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_st1d_horiz` + +""" +function intr_st1d_horiz( + predicate::Value, + store_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, store_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.st1d.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_st1d_vert` + +""" +function intr_st1d_vert( + predicate::Value, + store_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, store_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.st1d.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_st1h_horiz` + +""" +function intr_st1h_horiz( + predicate::Value, + store_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, store_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.st1h.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_st1h_vert` + +""" +function intr_st1h_vert( + predicate::Value, + store_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, store_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.st1h.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_st1q_horiz` + +""" +function intr_st1q_horiz( + predicate::Value, + store_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, store_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.st1q.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_st1q_vert` + +""" +function intr_st1q_vert( + predicate::Value, + store_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, store_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.st1q.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_st1w_horiz` + +""" +function intr_st1w_horiz( + predicate::Value, + store_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, store_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.st1w.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_st1w_vert` + +""" +function intr_st1w_vert( + predicate::Value, + store_address::Value, + tile_slice_index::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[predicate, store_address, tile_slice_index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.st1w.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_str` + +""" +function intr_str(index::Value, store_address::Value, offset::Value; location=Location()) + _results = IR.Type[] + _operands = Value[index, store_address, offset] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sme.intr.str", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_sumopa_wide` + +""" +function intr_sumopa_wide( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.sumopa.wide", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_sumops_wide` + +""" +function intr_sumops_wide( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.sumops.wide", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_umopa_wide` + +""" +function intr_umopa_wide( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.umopa.wide", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_umopa_za32` + +""" +function intr_umopa_za32( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.umopa.za32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_umops_wide` + +""" +function intr_umops_wide( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.umops.wide", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_umops_za32` + +""" +function intr_umops_za32( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.umops.za32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_usmopa_wide` + +""" +function intr_usmopa_wide( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.usmopa.wide", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_usmops_wide` + +""" +function intr_usmops_wide( + lhs_predicate::Value, + rhs_predicate::Value, + lhs_vector::Value, + rhs_vector::Value; + tile_id, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs_predicate, rhs_predicate, lhs_vector, rhs_vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.usmops.wide", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_write_horiz` + +""" +function intr_write_horiz( + tile_slice_index::Value, predicate::Value, vector::Value; tile_id, location=Location() +) + _results = IR.Type[] + _operands = Value[tile_slice_index, predicate, vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.write.horiz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_write_vert` + +""" +function intr_write_vert( + tile_slice_index::Value, predicate::Value, vector::Value; tile_id, location=Location() +) + _results = IR.Type[] + _operands = Value[tile_slice_index, predicate, vector] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_id", tile_id),] + + return IR.create_operation( + "arm_sme.intr.write.vert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_zero` + +""" +function intr_zero(; tile_mask, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("tile_mask", tile_mask),] + + return IR.create_operation( + "arm_sme.intr.zero", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # arm_sme diff --git a/src/Dialects/19/ArmSVE.jl b/src/Dialects/19/ArmSVE.jl new file mode 100644 index 00000000..5de864de --- /dev/null +++ b/src/Dialects/19/ArmSVE.jl @@ -0,0 +1,1093 @@ +module arm_sve + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`intr_convert_from_svbool` + +""" +function intr_convert_from_svbool(svbool::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[svbool,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.convert.from.svbool", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`convert_from_svbool` + +Converts svbool types (`vector<[16]xi1>` or vectors of that type, e.g. +`vector<2x3x[16]xi1>`) to SVE predicate types. Note: Only the trailing +dimension can be scalable. + +Example 1: Convert a 1-D svbool mask to a SVE predicate. +```mlir +%source = vector.load %memref[%c0] : memref, vector<[16]xi1> +%result = arm_sve.convert_from_svbool %source : vector<[4]xi1> +``` + +Example 2: Convert a 2-D svbool mask to a mask of SVE predicates. +```mlir +%source = vector.load %memref[%c0, %c0] : memref<2x?xi1>, vector<2x[16]xi1> +%result = arm_sve.convert_from_svbool %source : vector<2x[8]xi1> +``` + +--- + +A `svbool` is the smallest SVE predicate type that has a in-memory +representation (and maps to a full predicate register). In MLIR `svbool` is +represented as `vector<[16]xi1>`. Smaller SVE predicate types +(`vector<[1|2|4|8]xi1>`) must be stored as a `svbool` then converted back to +the original predicate type after loading. +""" +function convert_from_svbool(source::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.convert_from_svbool", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_convert_to_svbool` + +""" +function intr_convert_to_svbool(mask::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[mask,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.convert.to.svbool", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`convert_to_svbool` + +Converts SVE predicate types (or vectors of predicate types, e.g. +`vector<4x[4]xi1>`) to svbool types. Note: Only the trailing dimension can +be scalable. + +Example 1: Convert a 1-D SVE predicate to a svbool mask. +```mlir +%source = vector.create_mask %dim_size : vector<[4]xi1> +%result = arm_sve.convert_to_svbool %source : vector<[4]xi1> +// => Results in vector<[16]xi1> +``` + +Example 2: Convert a 2-D mask of SVE predicates to a svbool mask. +```mlir +%source = vector.create_mask %c2, %dim_size : vector<2x[2]xi1> +%result = arm_sve.convert_to_svbool %source : vector<2x[2]xi1> +// => Results in vector<2x[16]xi1> +``` + +--- + +A `svbool` is the smallest SVE predicate type that has a in-memory +representation (and maps to a full predicate register). In MLIR `svbool` is +represented as `vector<[16]xi1>`. Smaller SVE predicate types +(`vector<[1|2|4|8]xi1>`) must be converted to a `svbool` before they can be +stored. +""" +function convert_to_svbool(source::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.convert_to_svbool", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_psel` + +""" +function intr_psel(p1::Value, p2::Value, index::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[p1, p2, index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.psel", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`psel` + +This operation returns the input predicate `p1` or an all-false predicate +based on the bit at `p2[index]`. Informally, the semantics are: +``` +if p2[index % num_elements(p2)] == 1: + return p1 : type(p1) +return all-false : type(p1) +``` + +# Example +```mlir +// Note: p1 and p2 can have different sizes. +%pd = arm_sve.psel %p1, %p2[%index] : vector<[4]xi1>, vector<[8]xi1> +``` + +Note: This requires SME or SVE2.1 (`+sme` or `+sve2p1` in LLVM target features). +""" +function psel(p1::Value, p2::Value, index::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[p1, p2, index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.psel", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_fadd` + +""" +function intr_fadd( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.fadd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`masked_addf` + +The `arm_sve.masked.addf` operation takes one scalable vector mask +and two scalable vector operands, and perform floating point addition on active lanes. Inactive lanes will keep the value of +the first operand. +""" +function masked_addf( + mask::Value, src1::Value, src2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[mask, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.masked.addf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_add` + +""" +function intr_add( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`masked_addi` + +The `arm_sve.masked.addi` operation takes one scalable vector mask +and two scalable vector operands, and perform integer addition on active lanes. Inactive lanes will keep the value of +the first operand. +""" +function masked_addi( + mask::Value, src1::Value, src2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[mask, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.masked.addi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_fdiv` + +""" +function intr_fdiv( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.fdiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`masked_divf` + +The `arm_sve.masked.divf` operation takes one scalable vector mask +and two scalable vector operands, and perform floating point division on active lanes. Inactive lanes will keep the value of +the first operand. +""" +function masked_divf( + mask::Value, src1::Value, src2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[mask, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.masked.divf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_fmul` + +""" +function intr_fmul( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.fmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`masked_mulf` + +The `arm_sve.masked.mulf` operation takes one scalable vector mask +and two scalable vector operands, and perform floating point multiplication on active lanes. Inactive lanes will keep the value of +the first operand. +""" +function masked_mulf( + mask::Value, src1::Value, src2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[mask, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.masked.mulf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_mul` + +""" +function intr_mul( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`masked_muli` + +The `arm_sve.masked.muli` operation takes one scalable vector mask +and two scalable vector operands, and perform integer multiplication on active lanes. Inactive lanes will keep the value of +the first operand. +""" +function masked_muli( + mask::Value, src1::Value, src2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[mask, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.masked.muli", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_sdiv` + +""" +function intr_sdiv( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.sdiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`masked_divi_signed` + +The `arm_sve.masked.divi_signed` operation takes one scalable vector mask +and two scalable vector operands, and perform integer signed division on active lanes. Inactive lanes will keep the value of +the first operand. +""" +function masked_divi_signed( + mask::Value, src1::Value, src2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[mask, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.masked.divi_signed", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_fsub` + +""" +function intr_fsub( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.fsub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`masked_subf` + +The `arm_sve.masked.subf` operation takes one scalable vector mask +and two scalable vector operands, and perform floating point subtraction on active lanes. Inactive lanes will keep the value of +the first operand. +""" +function masked_subf( + mask::Value, src1::Value, src2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[mask, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.masked.subf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_sub` + +""" +function intr_sub( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.sub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`masked_subi` + +The `arm_sve.masked.subi` operation takes one scalable vector mask +and two scalable vector operands, and perform integer subtraction on active lanes. Inactive lanes will keep the value of +the first operand. +""" +function masked_subi( + mask::Value, src1::Value, src2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[mask, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.masked.subi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_udiv` + +""" +function intr_udiv( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.udiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`masked_divi_unsigned` + +The `arm_sve.masked.divi_unsigned` operation takes one scalable vector mask +and two scalable vector operands, and perform integer unsigned division on active lanes. Inactive lanes will keep the value of +the first operand. +""" +function masked_divi_unsigned( + mask::Value, src1::Value, src2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[mask, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.masked.divi_unsigned", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_sdot` + +""" +function intr_sdot( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.sdot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sdot` + +SDOT: Signed integer addition of dot product. + +This function maps to the SDOT instruction, and it takes signless integer +operands that the operation interprets as signed. It partitions the second +and third vector inputs into groups of four elements. They calculate the dot +product of each group (without loss of precision) and then add each result +to the overlapping element of the first vector input. + +Source: +https://developer.arm.com/documentation/100987/0000 +""" +function sdot(acc::Value, src1::Value, src2::Value; dst::IR.Type, location=Location()) + _results = IR.Type[dst,] + _operands = Value[acc, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.sdot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_smmla` + +""" +function intr_smmla( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.smmla", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`smmla` + +SMMLA: Signed integer matrix multiply-accumulate. + +This function maps to the SMMLA instruction, and it takes signless integer +operands that the operation interprets as signed. It partitions the inputs +into 128-bit quadwords, with the first input containing a row-by-row 2×2 +matrix of 32-bit integers, the second input containing a row-by-row 2×8 +matrix of 8-bit integers, and the third input containing a column-by-column +8×2 matrix of 8-bit integers. For each quadword, they multiply the second +input matrix by the third input matrix using natural arithmetic and then add +the result to the first input using modular arithmetic. + +Source: +https://developer.arm.com/documentation/100987/0000 +""" +function smmla(acc::Value, src1::Value, src2::Value; dst::IR.Type, location=Location()) + _results = IR.Type[dst,] + _operands = Value[acc, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.smmla", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_udot` + +""" +function intr_udot( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.udot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`udot` + +UDOT: Unsigned integer addition of dot product. + +This function maps to the UDOT instruction, and it takes signless integer +operands that the operation interprets as unsigned. It partitions the second +and third vector inputs into groups of four elements. They calculate the dot +product of each group (without loss of precision) and then add each result +to the overlapping element of the first vector input. + +Source: +https://developer.arm.com/documentation/100987/0000 +""" +function udot(acc::Value, src1::Value, src2::Value; dst::IR.Type, location=Location()) + _results = IR.Type[dst,] + _operands = Value[acc, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.udot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ummla` + +""" +function intr_ummla( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.ummla", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ummla` + +UMMLA: Unsigned integer matrix multiply-accumulate. + +This function maps to the UMMLA instruction, and it takes signless integer +operands that the operation interprets as unsigned. It partitions the inputs +into 128-bit quadwords, with the first input containing a row-by-row 2×2 +matrix of 32-bit integers, the second input containing a row-by-row 2×8 +matrix of 8-bit integers, and the third input containing a column-by-column +8×2 matrix of 8-bit integers. For each quadword, they multiply the second +input matrix by the third input matrix using natural arithmetic and then add +the result to the first input using modular arithmetic. + +Source: +https://developer.arm.com/documentation/100987/0000 +""" +function ummla(acc::Value, src1::Value, src2::Value; dst::IR.Type, location=Location()) + _results = IR.Type[dst,] + _operands = Value[acc, src1, src2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.ummla", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_whilelt` + +""" +function intr_whilelt(base::Value, n::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[base, n] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.whilelt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_zip_x2` + +""" +function intr_zip_x2(v1::Value, v2::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[v1, v2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.zip.x2", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`zip_x2` + +This operation interleaves elements from two input SVE vectors, returning +two new SVE vectors (`resultV1` and `resultV2`), which contain the low and +high halves of the result respectively. + +# Example +```mlir +// sourceV1 = [ A1, A2, A3, ... An ] +// sourceV2 = [ B1, B2, B3, ... Bn ] +// (resultV1, resultV2) = [ A1, B1, A2, B2, A3, B3, ... An, Bn ] +%resultV1, %resultV2 = arm_sve.zip.x2 %sourceV1, %sourceV2 : vector<[16]xi8> +``` + +Note: This requires SME 2 (`+sme2` in LLVM target features) + +[Source](https://developer.arm.com/documentation/ddi0602/2023-12/SME-Instructions/ZIP--two-registers---Interleave-elements-from-two-vectors-?lang=en) +""" +function zip_x2( + sourceV1::Value, + sourceV2::Value; + resultV1::IR.Type, + resultV2::IR.Type, + location=Location(), +) + _results = IR.Type[resultV1, resultV2] + _operands = Value[sourceV1, sourceV2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.zip.x2", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_zip_x4` + +""" +function intr_zip_x4( + v1::Value, v2::Value, v3::Value, v4::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[v1, v2, v3, v4] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.intr.zip.x4", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`zip_x4` + +This operation interleaves elements from four input SVE vectors, returning +four new SVE vectors, each of which contain a quarter of the result. The +first quarter will be in `resultV1`, second in `resultV2`, third in +`resultV3`, and fourth in `resultV4`. + +```mlir +// sourceV1 = [ A1, A2, ... An ] +// sourceV2 = [ B1, B2, ... Bn ] +// sourceV3 = [ C1, C2, ... Cn ] +// sourceV4 = [ D1, D2, ... Dn ] +// (resultV1, resultV2, resultV3, resultV4) +// = [ A1, B1, C1, D1, A2, B2, C2, D2, ... An, Bn, Cn, Dn ] +%resultV1, %resultV2, %resultV3, %resultV4 = arm_sve.zip.x4 + %sourceV1, %sourceV2, %sourceV3, %sourceV4 : vector<[16]xi8> +``` + +**Warning:** The result of this op is undefined for 64-bit elements on +hardware with less than 256-bit vectors! + +Note: This requires SME 2 (`+sme2` in LLVM target features) + +[Source](https://developer.arm.com/documentation/ddi0602/2023-12/SME-Instructions/ZIP--four-registers---Interleave-elements-from-four-vectors-?lang=en) +""" +function zip_x4( + sourceV1::Value, + sourceV2::Value, + sourceV3::Value, + sourceV4::Value; + resultV1::IR.Type, + resultV2::IR.Type, + resultV3::IR.Type, + resultV4::IR.Type, + location=Location(), +) + _results = IR.Type[resultV1, resultV2, resultV3, resultV4] + _operands = Value[sourceV1, sourceV2, sourceV3, sourceV4] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "arm_sve.zip.x4", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # arm_sve diff --git a/src/Dialects/19/Async.jl b/src/Dialects/19/Async.jl new file mode 100644 index 00000000..84750ab3 --- /dev/null +++ b/src/Dialects/19/Async.jl @@ -0,0 +1,916 @@ +module async + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`add_to_group` + +The `async.add_to_group` adds an async token or value to the async group. +Returns the rank of the added element in the group. This rank is fixed +for the group lifetime. + +# Example + +```mlir +%0 = async.create_group %size : !async.group +%1 = ... : !async.token +%2 = async.add_to_group %1, %0 : !async.token +``` +""" +function add_to_group( + operand::Value, group::Value; rank=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand, group] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(rank) && push!(_results, rank) + + return IR.create_operation( + "async.add_to_group", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`await_all` + +The `async.await_all` operation waits until all the tokens or values in the +group become ready. + +# Example + +```mlir +%0 = async.create_group %size : !async.group + +%1 = ... : !async.token +%2 = async.add_to_group %1, %0 : !async.token + +%3 = ... : !async.token +%4 = async.add_to_group %2, %0 : !async.token + +async.await_all %0 +``` +""" +function await_all(operand::Value; location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.await_all", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`await` + +The `async.await` operation waits until the argument becomes ready, and for +the `async.value` arguments it unwraps the underlying value + +# Example + +```mlir +%0 = ... : !async.token +async.await %0 : !async.token + +%1 = ... : !async.value +%2 = async.await %1 : !async.value +``` +""" +function await(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "async.await", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`call` + +The `async.call` operation represents a direct call to an async function +that is within the same symbol scope as the call. The operands and result +types of the call must match the specified async function type. The callee +is encoded as a symbol reference attribute named \"callee\". + +# Example + +```mlir +%2 = async.call @my_add(%0, %1) : (f32, f32) -> !async.value +``` +""" +function call( + operands::Vector{Value}; result_0::Vector{IR.Type}, callee, location=Location() +) + _results = IR.Type[result_0...,] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("callee", callee),] + + return IR.create_operation( + "async.call", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`coro_begin` + +The `async.coro.begin` allocates a coroutine frame and returns a handle to +the coroutine. +""" +function coro_begin(id::Value; handle=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[id,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(handle) && push!(_results, handle) + + return IR.create_operation( + "async.coro.begin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`coro_end` + +The `async.coro.end` marks the point where a coroutine needs to return +control back to the caller if it is not an initial invocation of the +coroutine. It the start part of the coroutine is is no-op. +""" +function coro_end(handle::Value; location=Location()) + _results = IR.Type[] + _operands = Value[handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.coro.end", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`coro_free` + +The `async.coro.free` deallocates the coroutine frame created by the +async.coro.begin operation. +""" +function coro_free(id::Value, handle::Value; location=Location()) + _results = IR.Type[] + _operands = Value[id, handle] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.coro.free", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`coro_id` + +The `async.coro.id` returns a switched-resume coroutine identifier. +""" +function coro_id(; id=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(id) && push!(_results, id) + + return IR.create_operation( + "async.coro.id", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`coro_save` + +The `async.coro.saves` saves the coroutine state. +""" +function coro_save( + handle::Value; state=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(state) && push!(_results, state) + + return IR.create_operation( + "async.coro.save", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`coro_suspend` + +The `async.coro.suspend` suspends the coroutine and transfers control to the +`suspend` successor. If suspended coroutine later resumed it will transfer +control to the `resume` successor. If it is destroyed it will transfer +control to the the `cleanup` successor. + +In switched-resume lowering coroutine can be already in resumed state when +suspend operation is called, in this case control will be transferred to the +`resume` successor skipping the `suspend` successor. +""" +function coro_suspend( + state::Value; + suspendDest::Block, + resumeDest::Block, + cleanupDest::Block, + location=Location(), +) + _results = IR.Type[] + _operands = Value[state,] + _owned_regions = Region[] + _successors = Block[suspendDest, resumeDest, cleanupDest] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.coro.suspend", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_group` + +The `async.create_group` allocates an empty async group. Async tokens or +values can be added to this group later. The size of the group must be +specified at construction time, and `await_all` operation will first +wait until the number of added tokens or values reaches the group size. + +# Example + +```mlir +%size = ... : index +%group = async.create_group %size : !async.group +... +async.await_all %group +``` +""" +function create_group( + size::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[size,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "async.create_group", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`execute` + +The `body` region attached to the `async.execute` operation semantically +can be executed concurrently with the successor operation. In the followup +example \"compute0\" can be executed concurrently with \"compute1\". + +The actual concurrency semantics depends on the dialect lowering to the +executable format. Fully sequential execution (\"compute0\" completes before +\"compute1\" starts) is a completely legal execution. + +Because concurrent execution is not guaranteed, it is illegal to create an +implicit dependency from \"compute1\" to \"compute0\" (e.g. via shared global +state). All dependencies must be made explicit with async execute arguments +(`async.token` or `async.value`). + + `async.execute` operation takes `async.token` dependencies and `async.value` +operands separately, and starts execution of the attached body region only +when all tokens and values become ready. + +# Example + +```mlir +%dependency = ... : !async.token +%value = ... : !async.value + +%token, %results = + async.execute [%dependency](%value as %unwrapped: !async.value) + -> !async.value + { + %0 = \"compute0\"(%unwrapped): (f32) -> !some.type + async.yield %0 : !some.type + } + +%1 = \"compute1\"(...) : !some.type +``` + +In the example above asynchronous execution starts only after dependency +token and value argument become ready. Unwrapped value passed to the +attached body region as an %unwrapped value of f32 type. +""" +function execute( + dependencies::Vector{Value}, + bodyOperands::Vector{Value}; + token::IR.Type, + bodyResults::Vector{IR.Type}, + bodyRegion::Region, + location=Location(), +) + _results = IR.Type[token, bodyResults...] + _operands = Value[dependencies..., bodyOperands...] + _owned_regions = Region[bodyRegion,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(dependencies), length(bodyOperands)])) + + return IR.create_operation( + "async.execute", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`func` + +An async function is like a normal function, but supports non-blocking +await. Internally, async function is lowered to the LLVM coroutinue with +async runtime intrinsic. It can return an async token and/or async values. +The token represents the execution state of async function and can be used +when users want to express dependencies on some side effects, e.g., +the token becomes available once every thing in the func body is executed. + +# Example + +```mlir +// Async function can\'t return void, it always must be some async thing. +async.func @async.0() -> !async.token { + return +} + +// Function returns only async value. +async.func @async.1() -> !async.value { + %0 = arith.constant 42 : i32 + return %0 : i32 +} + +// Implicit token can be added to return types. +async.func @async.2() -> !async.token, !async.value { + %0 = arith.constant 42 : i32 + return %0 : i32 +} +``` +""" +function func(; + sym_name, + function_type, + sym_visibility=nothing, + arg_attrs=nothing, + res_attrs=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("function_type", function_type) + ] + !isnothing(sym_visibility) && + push!(_attributes, namedattribute("sym_visibility", sym_visibility)) + !isnothing(arg_attrs) && push!(_attributes, namedattribute("arg_attrs", arg_attrs)) + !isnothing(res_attrs) && push!(_attributes, namedattribute("res_attrs", res_attrs)) + + return IR.create_operation( + "async.func", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`return_` + +The `async.return` is a special terminator operation for Async function. + +# Example + +```mlir +async.func @foo() : !async.token { + return +} +``` +""" +function return_(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.return", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`runtime_add_ref` + +The `async.runtime.add_ref` operation adds a reference(s) to async value +(token, value or group). +""" +function runtime_add_ref(operand::Value; count, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("count", count),] + + return IR.create_operation( + "async.runtime.add_ref", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`runtime_add_to_group` + +The `async.runtime.add_to_group` adds an async token or value to the async +group. Returns the rank of the added element in the group. +""" +function runtime_add_to_group( + operand::Value, group::Value; rank=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand, group] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(rank) && push!(_results, rank) + + return IR.create_operation( + "async.runtime.add_to_group", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`runtime_await_and_resume` + +The `async.runtime.await_and_resume` operation awaits for the operand to +become available or error and resumes the coroutine on a thread managed by +the runtime. +""" +function runtime_await_and_resume(operand::Value, handle::Value; location=Location()) + _results = IR.Type[] + _operands = Value[operand, handle] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.runtime.await_and_resume", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`runtime_await` + +The `async.runtime.await` operation blocks the caller thread until the +operand becomes available or error. +""" +function runtime_await(operand::Value; location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.runtime.await", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`runtime_create_group` + +The `async.runtime.create_group` operation creates an async dialect group +of the given size. Group created in the empty state. +""" +function runtime_create_group( + size::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[size,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "async.runtime.create_group", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`runtime_create` + +The `async.runtime.create` operation creates an async dialect token or +value. Tokens and values are created in the non-ready state. +""" +function runtime_create(; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.runtime.create", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`runtime_drop_ref` + +The `async.runtime.drop_ref` operation drops a reference(s) to async value +(token, value or group). +""" +function runtime_drop_ref(operand::Value; count, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("count", count),] + + return IR.create_operation( + "async.runtime.drop_ref", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`runtime_is_error` + +The `async.runtime.is_error` operation returns true if the token, value or +group (any of the async runtime values) is in the error state. It is the +caller responsibility to check error state after the call to `await` or +resuming after `await_and_resume`. +""" +function runtime_is_error( + operand::Value; is_error=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(is_error) && push!(_results, is_error) + + return IR.create_operation( + "async.runtime.is_error", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`runtime_load` + +The `async.runtime.load` operation loads the value from the runtime +async.value storage. +""" +function runtime_load( + storage::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[storage,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "async.runtime.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`runtime_num_worker_threads` + +The `async.runtime.num_worker_threads` operation gets the number of threads +in the threadpool from the runtime. +""" +function runtime_num_worker_threads(; + result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "async.runtime.num_worker_threads", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`runtime_resume` + +The `async.runtime.resume` operation resumes the coroutine on a thread +managed by the runtime. +""" +function runtime_resume(handle::Value; location=Location()) + _results = IR.Type[] + _operands = Value[handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.runtime.resume", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`runtime_set_available` + +The `async.runtime.set_available` operation switches async token or value +state to available. +""" +function runtime_set_available(operand::Value; location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.runtime.set_available", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`runtime_set_error` + +The `async.runtime.set_error` operation switches async token or value +state to error. +""" +function runtime_set_error(operand::Value; location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.runtime.set_error", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`runtime_store` + +The `async.runtime.store` operation stores the value into the runtime +async.value storage. +""" +function runtime_store(value::Value, storage::Value; location=Location()) + _results = IR.Type[] + _operands = Value[value, storage] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.runtime.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +The `async.yield` is a special terminator operation for the block inside +`async.execute` operation. +""" +function yield(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "async.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # async diff --git a/src/Dialects/19/Bufferization.jl b/src/Dialects/19/Bufferization.jl new file mode 100644 index 00000000..d7684049 --- /dev/null +++ b/src/Dialects/19/Bufferization.jl @@ -0,0 +1,453 @@ +module bufferization + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`alloc_tensor` + +`bufferization.alloc_tensor` materializes an uninitialized tensor with a +given shape (dynamic or static). It always bufferizes to a new buffer +allocation of the given shape. The optional `copy` operand specifies the +contents of the tensors. If no `copy` operand is specified, reading from the +result of an `alloc_tensor` op yields an undefined value. + +If `copy` is specified, no dynamic sizes should be passed, since they are +the same as the dynamic sizes of the `copy` operand. + +`alloc_tensor` is a helper op for bufferization. The operation is provided +as an anchor that marks the beginning of a new tensor SSA use-def chain. It +can be used to control in-place bufferization decisions during One-Shot +Bufferize: The bufferized result of a `bufferization.alloc_tensor` does not +alias with any other buffer, so it can be used to resolve read-after-write +conflicts that would have been introduced by the in-place bufferization of +another op. + +The optional `memory_space` attribute specifies the memory space when +bufferizing this op. The memory space is inferred from `copy` if specified. +If neither `copy` nor `memory_space` is specified, the default memory space +is used during bufferization. + +The optional `size_hint` operand specifies the number of non-zero elements +for sparse tensors. The value of `size_hint` should be not less than 1 and +not larger than the linear size of the corresponding dense tensor type. If +this requirement is not met, the behavior of the operator is undefined. + +Both dense and sparse tensor types are supported. The result of a +`bufferization.alloc_tensor` is a tensor value that can be used like any +other tensor value. In practice, it is often used as the \"out\" operand of +another op. Sparse tensor allocations should always be used in a local +construction operation and never escape the function boundary directly. + +# Example + +```mlir +%c = bufferization.alloc_tensor(%d1, %d2) : tensor +%0 = linalg.matmul + ins(%a, %b: tensor, tensor) + outs(%c: tensor) -> tensor +return %0 : tensor +``` + +```mlir +%c = bufferization.alloc_tensor(%d1, %d2) size_hint = %noe + : tensor +``` + +Note: An `alloc_tensor` with a `copy` should also be expressed as an +`alloc_tensor` without `copy`, followed by a `copy_tensor`. +""" +function alloc_tensor( + dynamic_sizes::Vector{Value}, + copy=nothing::Union{Nothing,Value}; + size_hint=nothing::Union{Nothing,Value}, + result::IR.Type, + memory_space=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[dynamic_sizes...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(copy) && push!(_operands, copy) + !isnothing(size_hint) && push!(_operands, size_hint) + push!( + _attributes, + operandsegmentsizes([ + length(dynamic_sizes), isnothing(copy) ? 0 : 1, isnothing(size_hint) ? 0 : 1 + ]), + ) + !isnothing(memory_space) && + push!(_attributes, namedattribute("memory_space", memory_space)) + + return IR.create_operation( + "bufferization.alloc_tensor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`clone` + +Clones the data in the input view into an implicitly defined output view. + +Usage: + +```mlir +%arg1 = bufferization.clone %arg0 : memref to memref +``` + +Valid implementations of this operation may alias the input and output +views or create an actual copy. Mutating the source or result +of the clone operation after the clone operation thus leads to undefined +behavior. +""" +function clone(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "bufferization.clone", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`dealloc` + +This operation deallocates each of the given memrefs if there is no alias +to that memref in the list of retained memrefs and the corresponding +condition value is set. This condition can be used to indicate and pass on +ownership of memref values (or in other words, the responsibility of +deallocating that memref). If two memrefs alias each other, only one will be +deallocated to avoid double free situations. + +The number of variadic `memref` operands (the memrefs to be deallocated) +must equal the number of variadic `condition` operands and correspond to +each other element-wise. + +The `memref` operands must be the originally allocated memrefs, however, the +`retained` memref operands may be arbitrary memrefs. + +This operation returns a variadic number of `updatedConditions` operands, +one updated condition per retained memref. An updated condition indicates +the ownership of the respective retained memref. It is computed as the +disjunction of all `conditions` operands where the corresponding to +`memrefs` operand aliases with the retained memref. If the retained memref +has no aliases among `memrefs`, the resulting updated condition is \'false\'. +This is because all memrefs that need to be deallocated within one basic +block should be added to the same `bufferization.dealloc` operation at the +end of the block; if no aliasing memref is present, then it does not have to +be deallocated and thus we don\'t need to claim ownership. If the memrefs to +be deallocated are split over multiple dealloc operations (e.g., to avoid +aliasing checks at runtime between the `memref` operands), then the results +have to be manually combined using an `arith.ori` operation and all of them +still require the same list of `retained` memref operands unless the +(potentially empty) set of aliasing memrefs can be determined statically. In +that case, the `updatedCondition` operand can be replaced accordingly (e.g., +by a canonicalizer). + +# Example +```mlir +%0:3 = bufferization.dealloc (%a0, %a1 : memref<2xf32>, memref<4xi32>) + if (%cond0, %cond1) retain (%r0, %r1, %r2 : memref, memref, + memref<2xi32>) +``` +Deallocation will be called on `%a0` if `%cond0` is \'true\' and neither +`%r0`, `%r1`, or `%r2` are aliases of `%a0`. `%a1` will be deallocated when +`%cond1` is set to \'true\' and none of `%r0`, %r1`, `%r2`, and `%a0` are +aliases. + +Note that this can be an expensive operation if there are many operands that +cannot be optimized away. The runtime cost of this operation (assuming that +nothing is optimized away) is `O(|memrefs|^2+|memrefs|*|retained|)`. The +cost in terms of memory space is `O(|memrefs|+|retained|)`. As a result, it +is recommended to place it carefully in the IR such that most operands can +be optimized away by running the `buffer-deallocation-simplification` pass. +""" +function dealloc( + memrefs::Vector{Value}, + conditions::Vector{Value}, + retained::Vector{Value}; + updatedConditions=nothing::Union{Nothing,Vector{IR.Type}}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[memrefs..., conditions..., retained...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + push!( + _attributes, + operandsegmentsizes([length(memrefs), length(conditions), length(retained)]), + ) + !isnothing(updatedConditions) && push!(_results, updatedConditions...) + + return IR.create_operation( + "bufferization.dealloc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`dealloc_tensor` + +`bufferization.dealloc_tensor` is a buffer deallocation in tensor land. This +op can be used for manual buffer deallocation. Some bufferizations (such as +One-Shot Bufferize) take care of buffer deallocation, in which case this op +is usually not needed. Details can be found in the documentation of the +respective bufferization passes. + +In case of a dense tensor, this op lowers to a `memref.dealloc` op during +bufferization. + +In case of a sparse tensor, this op releases the underlying sparse storage +format for a tensor that materialized earlier through a `new` operation, a +`convert` operation with annotated destination tensor type (unless the +convert is folded away), or a `bufferization.alloc_tensor` operation. The +release operation should only be called once for any materialized tensor. +After this operation, any subsequent `memref` querying operation on the +tensor returns undefined results. + +# Example + +```mlir +bufferization.dealloc_tensor %tensor : tensor<1024x1024xf64, #CSR> +``` +""" +function dealloc_tensor(tensor::Value; location=Location()) + _results = IR.Type[] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "bufferization.dealloc_tensor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`materialize_in_destination` + +This op indicates that the data of the `source` tensor is guaranteed to +materialize in `dest`, which can be a tensor or a memref. In case of a +tensor, `source` materializes in the future buffer of `dest` and a the +updated destination tensor is returned. If this is not possible, e.g., +because the destination tensor is read-only or because its original +contents are still read later, the input IR fails to bufferize. In case of a +memref, `source` materializes in `dest`, which is already a buffer. The op +has no results in that case. + +`source`, `dest` and `result` (if present) must have the same runtime shape +and element type. If the op has a result, the types of `result` and `dest` +must match exactly (e.g., including any tensor encodings). + +By default, this op bufferizes to a memcpy from the future buffer of the +`source` tensor to the future buffer of the `dest` tensor or to the `dest` +buffer. However, transformations such as \"empty tensor elimination\" may +rewrite IR such that a computation is performed directly in `dest` and no +memcpy is needed. + +If `dest` is a buffer, the `writable` attribute must be specified and the +`restrict` keyword can be specified. These attributes have the same meaning +as the respective attributes of `bufferization.to_tensor`. + +`writable` indicates that the `dest` buffer is considered writable. It does +not make sense to materialize a computation in a read-only buffer, so +`writable` is required. + +`restrict` indicates that there is no `bufferization.to_tensor` op and no +other `bufferization.materialize_in_destination` op with `dest` (or an alias +thereof) and \"restrict\". Only ops with this attribute are considered for +\"empty tensor elimination\". As part of empty tensor elimination, a new +`to_tensor` op with `dest` may be inserted and the `restrict` attribute is +transferred from this op to the new `to_tensor` op. Having \"restrict\" on +this op guarantees that performing empty tensor elimination would not create +invalid IR (i.e., having multiple `to_tensor restrict` with aliasing +buffers). + +Note: `writable` could be removed from this op because it must always be set +for memref destinations. This op has that attribute to make clear the +requirements on the `dest` operand in the op assembly format. + +Note: If `dest` is a tensor, `tensor.insert_slice` could be used for the +same purpose, but since tensor dialect ops only indicate *what* should be +computed but not *where*, it could fold away, causing the computation to +materialize in a different buffer. +""" +function materialize_in_destination( + source::Value, + dest::Value; + result=nothing::Union{Nothing,IR.Type}, + restrict=nothing, + writable=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source, dest] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(restrict) && push!(_attributes, namedattribute("restrict", restrict)) + !isnothing(writable) && push!(_attributes, namedattribute("writable", writable)) + + return IR.create_operation( + "bufferization.materialize_in_destination", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`to_memref` + +An operation that returns the future buffer of a `tensor`. + +```mlir +// Result type is memref<4x?xf32, #layout, 0> +%m = bufferization.to_memref %t : memref<4x?xf32, #layout, 0> +``` + +This operation is a specialized variant of the built-in +`unrealized_conversion_cast` and is used to make sure that the IR stays +valid at any point during the bufferization. + +The `read_only` attribute can optionally be set, indicating to the +bufferization that the buffer returned by this op (or an alias created from +the returned buffer) will not be written to. +""" +function to_memref(tensor::Value; memref::IR.Type, read_only=nothing, location=Location()) + _results = IR.Type[memref,] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(read_only) && push!(_attributes, namedattribute("read_only", read_only)) + + return IR.create_operation( + "bufferization.to_memref", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`to_tensor` + +An operation that creates a tensor from a `memref`. The result value is a +tensor whose shape and element type match the memref operand. + +The opposite of this op is `to_memref`. Together, these two ops are +useful for source/target materializations when doing type conversions +involving tensors and memrefs. + +# Example + +```mlir +// Produces a value of tensor<4x?xf32> type. +%t = bufferization.to_tensor %m : memref<4x?xf32, #layout, 0> +``` + +If the `writable` unit attribute is set, the produced tensor is considered +\"writable\" during bufferization. Otherwise, every OpOperand that bufferizes +to a write to the future buffer of the resulting tensor (or an alias +thereof) will bufferize out-of-place to prevent emitting any writes to +`memref` during bufferization. + +The `restrict` unit attribute (similar to the C `restrict` keyword) +indicates that the produced tensor result is the only way for the tensor +IR to gain access to the `memref` operand (or an alias thereof). E.g., +there must be no other `to_tensor` op with the same or with an aliasing +`memref` operand. + +Note: Only `to_tensor` ops with the `restrict` unit attribute are supported +by One-Shot Bufferize. Other IR is rejected. (To support `to_tensor` +without `restrict`, One-Shot Bufferize would have to analyze memref IR.) +Ops that have incorrect usage of `restrict` may bufferize incorrectly. + +# Example + +``` +%t = bufferization.to_tensor %m restrict writable : memref<4xf32> + +// %t is writable, so the tensor.insert may bufferize in-place in the +// absence of other conflicts. +%r = tensor.insert %f into %t[%idx] : tensor<4xf32> +``` + +`to_tensor` ops are not bufferized. They are expected to fold away after +bufferization. If there are non-bufferizable ops in the IR and +`allowUnknownOps` is set, they may be part of the resulting IR and not fold +away. However, such IR is no longer bufferizable with One-Shot Bufferize. +""" +function to_tensor( + memref::Value; + result=nothing::Union{Nothing,IR.Type}, + restrict=nothing, + writable=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[memref,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(restrict) && push!(_attributes, namedattribute("restrict", restrict)) + !isnothing(writable) && push!(_attributes, namedattribute("writable", writable)) + + return IR.create_operation( + "bufferization.to_tensor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +end # bufferization diff --git a/src/Dialects/19/Builtin.jl b/src/Dialects/19/Builtin.jl new file mode 100644 index 00000000..2ac242d8 --- /dev/null +++ b/src/Dialects/19/Builtin.jl @@ -0,0 +1,105 @@ +module builtin + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`module_` + +A `module` represents a top-level container operation. It contains a single +[graph region](../LangRef.md#control-flow-and-ssacfg-regions) containing a single block +which can contain any operations and does not have a terminator. Operations +within this region cannot implicitly capture values defined outside the module, +i.e. Modules are [IsolatedFromAbove](../Traits.md#isolatedfromabove). Modules have +an optional [symbol name](../SymbolsAndSymbolTables.md) which can be used to refer +to them in operations. + +# Example + +```mlir +module { + func.func @foo() +} +``` +""" +function module_(; + sym_name=nothing, sym_visibility=nothing, bodyRegion::Region, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[bodyRegion,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(sym_name) && push!(_attributes, namedattribute("sym_name", sym_name)) + !isnothing(sym_visibility) && + push!(_attributes, namedattribute("sym_visibility", sym_visibility)) + + return IR.create_operation( + "builtin.module", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`unrealized_conversion_cast` + +An `unrealized_conversion_cast` operation represents an unrealized +conversion from one set of types to another, that is used to enable the +inter-mixing of different type systems. This operation should not be +attributed any special representational or execution semantics, and is +generally only intended to be used to satisfy the temporary intermixing of +type systems during the conversion of one type system to another. + +This operation may produce results of arity 1-N, and accept as input +operands of arity 0-N. + +# Example + +```mlir +// An unrealized 0-1 conversion. These types of conversions are useful in +// cases where a type is removed from the type system, but not all uses have +// been converted. For example, imagine we have a tuple type that is +// expanded to its element types. If only some uses of an empty tuple type +// instance are converted we still need an instance of the tuple type, but +// have no inputs to the unrealized conversion. +%result = unrealized_conversion_cast to !bar.tuple_type<> + +// An unrealized 1-1 conversion. +%result1 = unrealized_conversion_cast %operand : !foo.type to !bar.lowered_type + +// An unrealized 1-N conversion. +%results2:2 = unrealized_conversion_cast %tuple_operand : !foo.tuple_type to !foo.type, !foo.type + +// An unrealized N-1 conversion. +%result3 = unrealized_conversion_cast %operand, %operand : !foo.type, !foo.type to !bar.tuple_type +``` +""" +function unrealized_conversion_cast( + inputs::Vector{Value}; outputs::Vector{IR.Type}, location=Location() +) + _results = IR.Type[outputs...,] + _operands = Value[inputs...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "builtin.unrealized_conversion_cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # builtin diff --git a/src/Dialects/19/Complex.jl b/src/Dialects/19/Complex.jl new file mode 100644 index 00000000..723ea132 --- /dev/null +++ b/src/Dialects/19/Complex.jl @@ -0,0 +1,1034 @@ +module complex + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`abs` + +The `abs` op takes a single complex number and computes its absolute value. + +# Example + +```mlir +%a = complex.abs %b : complex +``` +""" +function abs( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.abs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`add` + +The `add` operation takes two complex numbers and returns their sum. + +# Example + +```mlir +%a = complex.add %b, %c : complex +``` +""" +function add( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`angle` + +The `angle` op takes a single complex number and computes its argument value with a branch cut along the negative real axis. + +# Example + +```mlir + %a = complex.angle %b : complex +``` +""" +function angle( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.angle", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`atan2` + +For complex numbers it is expressed using complex logarithm +atan2(y, x) = -i * log((x + i * y) / sqrt(x**2 + y**2)) + +# Example + +```mlir +%a = complex.atan2 %b, %c : complex +``` +""" +function atan2( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.atan2", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`bitcast` + + +# Example + +```mlir + %a = complex.bitcast %b : complex -> i64 +``` +""" +function bitcast(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "complex.bitcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conj` + +The `conj` op takes a single complex number and computes the +complex conjugate. + +# Example + +```mlir +%a = complex.conj %b: complex +``` +""" +function conj( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.conj", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`constant` + +The `complex.constant` operation creates a constant complex number from an +attribute containing the real and imaginary parts. + +# Example + +```mlir +%a = complex.constant [0.1, -1.0] : complex +``` +""" +function constant(; complex::IR.Type, value, location=Location()) + _results = IR.Type[complex,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "complex.constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cos` + +The `cos` op takes a single complex number and computes the cosine of +it, i.e. `cos(x)`, where `x` is the input value. + +# Example + +```mlir +%a = complex.cos %b : complex +``` +""" +function cos( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.cos", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`create` + +The `complex.create` operation creates a complex number from two +floating-point operands, the real and the imaginary part. + +# Example + +```mlir +%a = complex.create %b, %c : complex +``` +""" +function create(real::Value, imaginary::Value; complex::IR.Type, location=Location()) + _results = IR.Type[complex,] + _operands = Value[real, imaginary] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "complex.create", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`div` + +The `div` operation takes two complex numbers and returns result of their +division: + +```mlir +%a = complex.div %b, %c : complex +``` +""" +function div( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.div", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`eq` + +The `eq` op takes two complex numbers and returns whether they are equal. + +# Example + +```mlir +%a = complex.eq %b, %c : complex +``` +""" +function eq( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "complex.eq", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`exp` + +The `exp` op takes a single complex number and computes the exponential of +it, i.e. `exp(x)` or `e^(x)`, where `x` is the input value. +`e` denotes Euler\'s number and is approximately equal to 2.718281. + +# Example + +```mlir +%a = complex.exp %b : complex +``` +""" +function exp( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.exp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`expm1` + +complex.expm1(x) := complex.exp(x) - 1 + +# Example + +```mlir +%a = complex.expm1 %b : complex +``` +""" +function expm1( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.expm1", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`im` + +The `im` op takes a single complex number and extracts the imaginary part. + +# Example + +```mlir +%a = complex.im %b : complex +``` +""" +function im( + complex::Value; + imaginary=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(imaginary) && push!(_results, imaginary) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.im", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`log1p` + +The `log` op takes a single complex number and computes the natural +logarithm of one plus the given value, i.e. `log(1 + x)` or `log_e(1 + x)`, +where `x` is the input value. `e` denotes Euler\'s number and is +approximately equal to 2.718281. + +# Example + +```mlir +%a = complex.log1p %b : complex +``` +""" +function log1p( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.log1p", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`log` + +The `log` op takes a single complex number and computes the natural +logarithm of it, i.e. `log(x)` or `log_e(x)`, where `x` is the input value. +`e` denotes Euler\'s number and is approximately equal to 2.718281. + +# Example + +```mlir +%a = complex.log %b : complex +``` +""" +function log( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.log", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mul` + +The `mul` operation takes two complex numbers and returns their product: + +```mlir +%a = complex.mul %b, %c : complex +``` +""" +function mul( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`neg` + +The `neg` op takes a single complex number `complex` and returns `-complex`. + +# Example + +```mlir +%a = complex.neg %b : complex +``` +""" +function neg( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.neg", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`neq` + +The `neq` op takes two complex numbers and returns whether they are not +equal. + +# Example + +```mlir +%a = complex.neq %b, %c : complex +``` +""" +function neq( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "complex.neq", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`pow` + +The `sqrt` operation takes a complex number raises it to the given complex +exponent. + +# Example + +```mlir +%a = complex.pow %b, %c : complex +``` +""" +function pow( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.pow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`re` + +The `re` op takes a single complex number and extracts the real part. + +# Example + +```mlir +%a = complex.re %b : complex +``` +""" +function re( + complex::Value; + real=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(real) && push!(_results, real) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.re", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`rsqrt` + +The `rsqrt` operation computes reciprocal of square root. + +# Example + +```mlir +%a = complex.rsqrt %b : complex +``` +""" +function rsqrt( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.rsqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sign` + +The `sign` op takes a single complex number and computes the sign of +it, i.e. `y = sign(x) = x / |x|` if `x != 0`, otherwise `y = 0`. + +# Example + +```mlir +%a = complex.sign %b : complex +``` +""" +function sign( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.sign", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sin` + +The `sin` op takes a single complex number and computes the sine of +it, i.e. `sin(x)`, where `x` is the input value. + +# Example + +```mlir +%a = complex.sin %b : complex +``` +""" +function sin( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.sin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sqrt` + +The `sqrt` operation takes a complex number and returns its square root. + +# Example + +```mlir +%a = complex.sqrt %b : complex +``` +""" +function sqrt( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.sqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sub` + +The `sub` operation takes two complex numbers and returns their difference. + +# Example + +```mlir +%a = complex.sub %b, %c : complex +``` +""" +function sub( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.sub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`tan` + +The `tan` op takes a single complex number and computes the tangent of +it, i.e. `tan(x)`, where `x` is the input value. + +# Example + +```mlir +%a = complex.tan %b : complex +``` +""" +function tan( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.tan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`tanh` + +The `tanh` operation takes a complex number and returns its hyperbolic +tangent. + +# Example + +```mlir +%a = complex.tanh %b : complex +``` +""" +function tanh( + complex::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[complex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "complex.tanh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +end # complex diff --git a/src/Dialects/19/ControlFlow.jl b/src/Dialects/19/ControlFlow.jl new file mode 100644 index 00000000..37b27684 --- /dev/null +++ b/src/Dialects/19/ControlFlow.jl @@ -0,0 +1,189 @@ +module cf + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`assert` + +Assert operation at runtime with single boolean operand and an error +message attribute. +If the argument is `true` this operation has no effect. Otherwise, the +program execution will abort. The provided error message may be used by a +runtime to propagate the error to the user. + +# Example + +```mlir +assert %b, \"Expected ... to be true\" +``` +""" +function assert(arg::Value; msg, location=Location()) + _results = IR.Type[] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("msg", msg),] + + return IR.create_operation( + "cf.assert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`br` + +The `cf.br` operation represents a direct branch operation to a given +block. The operands of this operation are forwarded to the successor block, +and the number and type of the operands must match the arguments of the +target block. + +# Example + +```mlir +^bb2: + %2 = call @someFn() + cf.br ^bb3(%2 : tensor<*xf32>) +^bb3(%3: tensor<*xf32>): +``` +""" +function br(destOperands::Vector{Value}; dest::Block, location=Location()) + _results = IR.Type[] + _operands = Value[destOperands...,] + _owned_regions = Region[] + _successors = Block[dest,] + _attributes = NamedAttribute[] + + return IR.create_operation( + "cf.br", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cond_br` + +The `cond_br` terminator operation represents a conditional branch on a +boolean (1-bit integer) value. If the bit is set, then the first destination +is jumped to; if it is false, the second destination is chosen. The count +and types of operands must align with the arguments in the corresponding +target blocks. + +The MLIR conditional branch operation is not allowed to target the entry +block for a region. The two destinations of the conditional branch operation +are allowed to be the same. + +The following example illustrates a function with a conditional branch +operation that targets the same block. + +# Example + +```mlir +func.func @select(%a: i32, %b: i32, %flag: i1) -> i32 { + // Both targets are the same, operands differ + cond_br %flag, ^bb1(%a : i32), ^bb1(%b : i32) + +^bb1(%x : i32) : + return %x : i32 +} +``` +""" +function cond_br( + condition::Value, + trueDestOperands::Vector{Value}, + falseDestOperands::Vector{Value}; + trueDest::Block, + falseDest::Block, + location=Location(), +) + _results = IR.Type[] + _operands = Value[condition, trueDestOperands..., falseDestOperands...] + _owned_regions = Region[] + _successors = Block[trueDest, falseDest] + _attributes = NamedAttribute[] + push!( + _attributes, + operandsegmentsizes([1, length(trueDestOperands), length(falseDestOperands)]), + ) + + return IR.create_operation( + "cf.cond_br", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`switch` + +The `switch` terminator operation represents a switch on a signless integer +value. If the flag matches one of the specified cases, then the +corresponding destination is jumped to. If the flag does not match any of +the cases, the default destination is jumped to. The count and types of +operands must align with the arguments in the corresponding target blocks. + +# Example + +```mlir +switch %flag : i32, [ + default: ^bb1(%a : i32), + 42: ^bb1(%b : i32), + 43: ^bb3(%c : i32) +] +``` +""" +function switch( + flag::Value, + defaultOperands::Vector{Value}, + caseOperands::Vector{Value}; + case_values=nothing, + case_operand_segments, + defaultDestination::Block, + caseDestinations::Vector{Block}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[flag, defaultOperands..., caseOperands...] + _owned_regions = Region[] + _successors = Block[defaultDestination, caseDestinations...] + _attributes = NamedAttribute[namedattribute( + "case_operand_segments", case_operand_segments + ),] + push!( + _attributes, operandsegmentsizes([1, length(defaultOperands), length(caseOperands)]) + ) + !isnothing(case_values) && + push!(_attributes, namedattribute("case_values", case_values)) + + return IR.create_operation( + "cf.switch", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # cf diff --git a/src/Dialects/19/EmitC.jl b/src/Dialects/19/EmitC.jl new file mode 100644 index 00000000..0ec7ba01 --- /dev/null +++ b/src/Dialects/19/EmitC.jl @@ -0,0 +1,1631 @@ +module emitc + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`add` + +With the `add` operation the arithmetic operator + (addition) can +be applied. + +# Example + +```mlir +// Custom form of the addition operation. +%0 = emitc.add %arg0, %arg1 : (i32, i32) -> i32 +%1 = emitc.add %arg2, %arg3 : (!emitc.ptr, i32) -> !emitc.ptr +``` +```c++ +// Code emitted for the operations above. +int32_t v5 = v1 + v2; +float* v6 = v3 + v4; +``` +""" +function add(lhs::Value, rhs::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply` + +With the `apply` operation the operators & (address of) and * (contents of) +can be applied to a single operand. + +# Example + +```mlir +// Custom form of applying the & operator. +%0 = emitc.apply \"&\"(%arg0) : (i32) -> !emitc.ptr + +// Generic form of the same operation. +%0 = \"emitc.apply\"(%arg0) {applicableOperator = \"&\"} + : (i32) -> !emitc.ptr + +``` +""" +function apply(operand::Value; result::IR.Type, applicableOperator, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("applicableOperator", applicableOperator),] + + return IR.create_operation( + "emitc.apply", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`assign` + +The `assign` operation stores an SSA value to the location designated by an +EmitC variable. This operation doesn\'t return any value. The assigned value +must be of the same type as the variable being assigned. The operation is +emitted as a C/C++ \'=\' operator. + +# Example + +```mlir +// Integer variable +%0 = \"emitc.variable\"(){value = 42 : i32} : () -> i32 +%1 = emitc.call_opaque \"foo\"() : () -> (i32) + +// Assign emitted as `... = ...;` +\"emitc.assign\"(%0, %1) : (i32, i32) -> () +``` +""" +function assign(var::Value, value::Value; location=Location()) + _results = IR.Type[] + _operands = Value[var, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.assign", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bitwise_and` + +With the `bitwise_and` operation the bitwise operator & (and) can +be applied. + +# Example + +```mlir +%0 = emitc.bitwise_and %arg0, %arg1 : (i32, i32) -> i32 +``` +```c++ +// Code emitted for the operation above. +int32_t v3 = v1 & v2; +``` +""" +function bitwise_and(lhs::Value, rhs::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.bitwise_and", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bitwise_left_shift` + +With the `bitwise_left_shift` operation the bitwise operator << +(left shift) can be applied. + +# Example + +```mlir +%0 = emitc.bitwise_left_shift %arg0, %arg1 : (i32, i32) -> i32 +``` +```c++ +// Code emitted for the operation above. +int32_t v3 = v1 << v2; +``` +""" +function bitwise_left_shift(lhs::Value, rhs::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.bitwise_left_shift", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bitwise_not` + +With the `bitwise_not` operation the bitwise operator ~ (not) can +be applied. + +# Example + +```mlir +%0 = emitc.bitwise_not %arg0 : (i32) -> i32 +``` +```c++ +// Code emitted for the operation above. +int32_t v2 = ~v1; +``` +""" +function bitwise_not(operand_0::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[operand_0,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.bitwise_not", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bitwise_or` + +With the `bitwise_or` operation the bitwise operator | (or) +can be applied. + +# Example + +```mlir +%0 = emitc.bitwise_or %arg0, %arg1 : (i32, i32) -> i32 +``` +```c++ +// Code emitted for the operation above. +int32_t v3 = v1 | v2; +``` +""" +function bitwise_or(lhs::Value, rhs::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.bitwise_or", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bitwise_right_shift` + +With the `bitwise_right_shift` operation the bitwise operator >> +(right shift) can be applied. + +# Example + +```mlir +%0 = emitc.bitwise_right_shift %arg0, %arg1 : (i32, i32) -> i32 +``` +```c++ +// Code emitted for the operation above. +int32_t v3 = v1 >> v2; +``` +""" +function bitwise_right_shift(lhs::Value, rhs::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.bitwise_right_shift", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bitwise_xor` + +With the `bitwise_xor` operation the bitwise operator ^ (xor) +can be applied. + +# Example + +```mlir +%0 = emitc.bitwise_xor %arg0, %arg1 : (i32, i32) -> i32 +``` +```c++ +// Code emitted for the operation above. +int32_t v3 = v1 ^ v2; +``` +""" +function bitwise_xor(lhs::Value, rhs::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.bitwise_xor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`call` + +The `emitc.call` operation represents a direct call to an `emitc.func` +that is within the same symbol scope as the call. The operands and result type +of the call must match the specified function type. The callee is encoded as a +symbol reference attribute named \"callee\". + +# Example + +```mlir +%2 = emitc.call @my_add(%0, %1) : (f32, f32) -> f32 +``` +""" +function call( + operands::Vector{Value}; result_0::Vector{IR.Type}, callee, location=Location() +) + _results = IR.Type[result_0...,] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("callee", callee),] + + return IR.create_operation( + "emitc.call", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`call_opaque` + +The `call_opaque` operation represents a C++ function call. The callee +can be an arbitrary non-empty string. The call allows specifying order +of operands and attributes in the call as follows: + +- integer value of index type refers to an operand; +- attribute which will get lowered to constant value in call; + +# Example + +```mlir +// Custom form defining a call to `foo()`. +%0 = emitc.call_opaque \"foo\" () : () -> i32 + +// Generic form of the same operation. +%0 = \"emitc.call_opaque\"() {callee = \"foo\"} : () -> i32 +``` +""" +function call_opaque( + operands::Vector{Value}; + result_0::Vector{IR.Type}, + callee, + args=nothing, + template_args=nothing, + location=Location(), +) + _results = IR.Type[result_0...,] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("callee", callee),] + !isnothing(args) && push!(_attributes, namedattribute("args", args)) + !isnothing(template_args) && + push!(_attributes, namedattribute("template_args", template_args)) + + return IR.create_operation( + "emitc.call_opaque", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cast` + +The `cast` operation performs an explicit type conversion and is emitted +as a C-style cast expression. It can be applied to integer, float, index +and EmitC types. + +# Example + +```mlir +// Cast from `int32_t` to `float` +%0 = emitc.cast %arg0: i32 to f32 + +// Cast from `void` to `int32_t` pointer +%1 = emitc.cast %arg1 : + !emitc.ptr> to !emitc.ptr +``` +""" +function cast(source::Value; dest::IR.Type, location=Location()) + _results = IR.Type[dest,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cmp` + +With the `cmp` operation the comparison operators ==, !=, <, <=, >, >=, <=> +can be applied. + +Its first argument is an attribute that defines the comparison operator: + +- equal to (mnemonic: `\"eq\"`; integer value: `0`) +- not equal to (mnemonic: `\"ne\"`; integer value: `1`) +- less than (mnemonic: `\"lt\"`; integer value: `2`) +- less than or equal to (mnemonic: `\"le\"`; integer value: `3`) +- greater than (mnemonic: `\"gt\"`; integer value: `4`) +- greater than or equal to (mnemonic: `\"ge\"`; integer value: `5`) +- three-way-comparison (mnemonic: `\"three_way\"`; integer value: `6`) + +# Example +```mlir +// Custom form of the cmp operation. +%0 = emitc.cmp eq, %arg0, %arg1 : (i32, i32) -> i1 +%1 = emitc.cmp lt, %arg2, %arg3 : + ( + !emitc.opaque<\"std::valarray\">, + !emitc.opaque<\"std::valarray\"> + ) -> !emitc.opaque<\"std::valarray\"> +``` +```c++ +// Code emitted for the operations above. +bool v5 = v1 == v2; +std::valarray v6 = v3 < v4; +``` +""" +function cmp(lhs::Value, rhs::Value; result_0::IR.Type, predicate, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("predicate", predicate),] + + return IR.create_operation( + "emitc.cmp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conditional` + +With the `conditional` operation the ternary conditional operator can +be applied. + +# Example + +```mlir +%0 = emitc.cmp gt, %arg0, %arg1 : (i32, i32) -> i1 + +%c0 = \"emitc.constant\"() {value = 10 : i32} : () -> i32 +%c1 = \"emitc.constant\"() {value = 11 : i32} : () -> i32 + +%1 = emitc.conditional %0, %c0, %c1 : i32 +``` +```c++ +// Code emitted for the operations above. +bool v3 = v1 > v2; +int32_t v4 = 10; +int32_t v5 = 11; +int32_t v6 = v3 ? v4 : v5; +``` +""" +function conditional( + condition::Value, + true_value::Value, + false_value::Value; + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[condition, true_value, false_value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.conditional", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`constant` + +The `constant` operation produces an SSA value equal to some constant +specified by an attribute. This can be used to form simple integer and +floating point constants, as well as more exotic things like tensor +constants. The `constant` operation also supports the EmitC opaque +attribute and the EmitC opaque type. Since folding is supported, +it should not be used with pointers. + +# Example + +```mlir +// Integer constant +%0 = \"emitc.constant\"(){value = 42 : i32} : () -> i32 + +// Constant emitted as `char = CHAR_MIN;` +%1 = \"emitc.constant\"() {value = #emitc.opaque<\"CHAR_MIN\">} + : () -> !emitc.opaque<\"char\"> +``` +""" +function constant(; result_0::IR.Type, value, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "emitc.constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`declare_func` + +The `declare_func` operation allows to insert a function declaration for an +`emitc.func` at a specific position. The operation only requires the `callee` +of the `emitc.func` to be specified as an attribute. + +# Example + +```mlir +emitc.declare_func @bar +emitc.func @foo(%arg0: i32) -> i32 { + %0 = emitc.call @bar(%arg0) : (i32) -> (i32) + emitc.return %0 : i32 +} + +emitc.func @bar(%arg0: i32) -> i32 { + emitc.return %arg0 : i32 +} +``` + +```c++ +// Code emitted for the operations above. +int32_t bar(int32_t v1); +int32_t foo(int32_t v1) { + int32_t v2 = bar(v1); + return v2; +} + +int32_t bar(int32_t v1) { + return v1; +} +``` +""" +function declare_func(; sym_name, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("sym_name", sym_name),] + + return IR.create_operation( + "emitc.declare_func", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`div` + +With the `div` operation the arithmetic operator / (division) can +be applied. + +# Example + +```mlir +// Custom form of the division operation. +%0 = emitc.div %arg0, %arg1 : (i32, i32) -> i32 +%1 = emitc.div %arg2, %arg3 : (f32, f32) -> f32 +``` +```c++ +// Code emitted for the operations above. +int32_t v5 = v1 / v2; +float v6 = v3 / v4; +``` +""" +function div(operand_0::Value, operand_1::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[operand_0, operand_1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.div", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`expression` + +The `expression` operation returns a single SSA value which is yielded by +its single-basic-block region. The operation doesn\'t take any arguments. + +As the operation is to be emitted as a C expression, the operations within +its body must form a single Def-Use tree of emitc ops whose result is +yielded by a terminating `emitc.yield`. + +# Example + +```mlir +%r = emitc.expression : i32 { + %0 = emitc.add %a, %b : (i32, i32) -> i32 + %1 = emitc.call_opaque \"foo\"(%0) : (i32) -> i32 + %2 = emitc.add %c, %d : (i32, i32) -> i32 + %3 = emitc.mul %1, %2 : (i32, i32) -> i32 + emitc.yield %3 : i32 +} +``` + +May be emitted as + +```c++ +int32_t v7 = foo(v1 + v2) * (v3 + v4); +``` + +The operations allowed within expression body are EmitC operations with the +CExpression trait. + +When specified, the optional `do_not_inline` indicates that the expression is +to be emitted as seen above, i.e. as the rhs of an EmitC SSA value +definition. Otherwise, the expression may be emitted inline, i.e. directly +at its use. +""" +function expression(; + result::IR.Type, do_not_inline=nothing, region::Region, location=Location() +) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(do_not_inline) && + push!(_attributes, namedattribute("do_not_inline", do_not_inline)) + + return IR.create_operation( + "emitc.expression", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`for_` + +The `emitc.for` operation represents a C loop of the following form: + +```c++ +for (T i = lb; i < ub; i += step) { /* ... */ } // where T is typeof(lb) +``` + +The operation takes 3 SSA values as operands that represent the lower bound, +upper bound and step respectively, and defines an SSA value for its +induction variable. It has one region capturing the loop body. The induction +variable is represented as an argument of this region. This SSA value is a +signless integer, or an index. The step is a value of same type. + +This operation has no result. The body region must contain exactly one block +that terminates with `emitc.yield`. Calling ForOp::build will create such a +region and insert the terminator implicitly if none is defined, so will the +parsing even in cases when it is absent from the custom format. For example: + +```mlir +// Index case. +emitc.for %iv = %lb to %ub step %step { + ... // body +} +... +// Integer case. +emitc.for %iv_32 = %lb_32 to %ub_32 step %step_32 : i32 { + ... // body +} +``` +""" +function for_( + lowerBound::Value, upperBound::Value, step::Value; region::Region, location=Location() +) + _results = IR.Type[] + _operands = Value[lowerBound, upperBound, step] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.for", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`func` + +Operations within the function cannot implicitly capture values defined +outside of the function, i.e. Functions are `IsolatedFromAbove`. All +external references must use function arguments or attributes that establish +a symbolic connection (e.g. symbols referenced by name via a string +attribute like SymbolRefAttr). While the MLIR textual form provides a nice +inline syntax for function arguments, they are internally represented as +“block arguments” to the first block in the region. + +Only dialect attribute names may be specified in the attribute dictionaries +for function arguments, results, or the function itself. + +# Example + +```mlir +// A function with no results: +emitc.func @foo(%arg0 : i32) { + emitc.call_opaque \"bar\" (%arg0) : (i32) -> () + emitc.return +} + +// A function with its argument as single result: +emitc.func @foo(%arg0 : i32) -> i32 { + emitc.return %arg0 : i32 +} + +// A function with specifiers attribute: +emitc.func @example_specifiers_fn_attr() -> i32 + attributes {specifiers = [\"static\",\"inline\"]} { + %0 = emitc.call_opaque \"foo\" (): () -> i32 + emitc.return %0 : i32 +} + +// An external function definition: +emitc.func private @extern_func(i32) + attributes {specifiers = [\"extern\"]} +``` +""" +function func(; + sym_name, + function_type, + specifiers=nothing, + arg_attrs=nothing, + res_attrs=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("function_type", function_type) + ] + !isnothing(specifiers) && push!(_attributes, namedattribute("specifiers", specifiers)) + !isnothing(arg_attrs) && push!(_attributes, namedattribute("arg_attrs", arg_attrs)) + !isnothing(res_attrs) && push!(_attributes, namedattribute("res_attrs", res_attrs)) + + return IR.create_operation( + "emitc.func", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_global` + +The `emitc.get_global` operation retrieves the lvalue of a +named global variable. If the global variable is marked constant, assigning +to that lvalue is undefined. + +# Example + +```mlir +%x = emitc.get_global @foo : !emitc.array<2xf32> +``` +""" +function get_global(; result::IR.Type, name, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("name", name),] + + return IR.create_operation( + "emitc.get_global", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`global_` + +The `emitc.global` operation declares or defines a named global variable. +The backing memory for the variable is allocated statically and is +described by the type of the variable. +Optionally, an `initial_value` can be provided. +Internal linkage can be specified using the `static_specifier` unit attribute +and external linkage can be specified using the `extern_specifier` unit attribute. +Note that the default linkage without those two keywords depends on whether +the target is C or C++ and whether the global variable is `const`. +The global variable can also be marked constant using the `const_specifier` +unit attribute. Writing to such constant global variables is +undefined. + +The global variable can be accessed by using the `emitc.get_global` to +retrieve the value for the global variable. + +# Example + +```mlir +// Global variable with an initial value. +emitc.global @x : emitc.array<2xf32> = dense<0.0, 2.0> +// External global variable +emitc.global extern @x : emitc.array<2xf32> +// Constant global variable with internal linkage +emitc.global static const @x : i32 = 0 +``` +""" +function global_(; + sym_name, + type, + initial_value=nothing, + extern_specifier=nothing, + static_specifier=nothing, + const_specifier=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("type", type) + ] + !isnothing(initial_value) && + push!(_attributes, namedattribute("initial_value", initial_value)) + !isnothing(extern_specifier) && + push!(_attributes, namedattribute("extern_specifier", extern_specifier)) + !isnothing(static_specifier) && + push!(_attributes, namedattribute("static_specifier", static_specifier)) + !isnothing(const_specifier) && + push!(_attributes, namedattribute("const_specifier", const_specifier)) + + return IR.create_operation( + "emitc.global", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`if_` + +The `if` operation represents an if-then-else construct for +conditionally executing two regions of code. The operand to an if operation +is a boolean value. For example: + +```mlir +emitc.if %b { + ... +} else { + ... +} +``` + +The \"then\" region has exactly 1 block. The \"else\" region may have 0 or 1 +blocks. The blocks are always terminated with `emitc.yield`, which can be +left out to be inserted implicitly. This operation doesn\'t produce any +results. +""" +function if_(condition::Value; thenRegion::Region, elseRegion::Region, location=Location()) + _results = IR.Type[] + _operands = Value[condition,] + _owned_regions = Region[thenRegion, elseRegion] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.if", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`include_` + +The `include` operation allows to define a source file inclusion via the +`#include` directive. + +# Example + +```mlir +// Custom form defining the inclusion of ``. +emitc.include <\"myheader.h\"> + +// Generic form of the same operation. +\"emitc.include\" (){include = \"myheader.h\", is_standard_include} : () -> () + +// Custom form defining the inclusion of `\"myheader\"`. +emitc.include \"myheader.h\" + +// Generic form of the same operation. +\"emitc.include\" (){include = \"myheader.h\"} : () -> () +``` +""" +function include_(; include_, is_standard_include=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("include", include_),] + !isnothing(is_standard_include) && + push!(_attributes, namedattribute("is_standard_include", is_standard_include)) + + return IR.create_operation( + "emitc.include", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`literal` + +The `literal` operation produces an SSA value equal to some constant +specified by an attribute. +""" +function literal(; result::IR.Type, value, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "emitc.literal", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`logical_and` + +With the `logical_and` operation the logical operator && (and) can +be applied. + +# Example + +```mlir +%0 = emitc.logical_and %arg0, %arg1 : i32, i32 +``` +```c++ +// Code emitted for the operation above. +bool v3 = v1 && v2; +``` +""" +function logical_and(lhs::Value, rhs::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.logical_and", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`logical_not` + +With the `logical_not` operation the logical operator ! (negation) can +be applied. + +# Example + +```mlir +%0 = emitc.logical_not %arg0 : i32 +``` +```c++ +// Code emitted for the operation above. +bool v2 = !v1; +``` +""" +function logical_not(operand_0::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[operand_0,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.logical_not", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`logical_or` + +With the `logical_or` operation the logical operator || (inclusive or) +can be applied. + +# Example + +```mlir +%0 = emitc.logical_or %arg0, %arg1 : i32, i32 +``` +```c++ +// Code emitted for the operation above. +bool v3 = v1 || v2; +``` +""" +function logical_or(lhs::Value, rhs::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.logical_or", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`member_of_ptr` + +With the `member_of_ptr` operation the member access operator `->` +can be applied. + +# Example + +```mlir +%0 = \"emitc.member_of_ptr\" (%arg0) {member = \"a\"} + : (!emitc.ptr>) -> i32 +``` +""" +function member_of_ptr(operand::Value; result_0::IR.Type, member, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("member", member),] + + return IR.create_operation( + "emitc.member_of_ptr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`member` + +With the `member` operation the member access operator `.` can be +applied. + +# Example + +```mlir +%0 = \"emitc.member\" (%arg0) {member = \"a\"} + : (!emitc.opaque<\"mystruct\">) -> i32 +``` +""" +function member(operand::Value; result_0::IR.Type, member, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("member", member),] + + return IR.create_operation( + "emitc.member", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mul` + +With the `mul` operation the arithmetic operator * (multiplication) can +be applied. + +# Example + +```mlir +// Custom form of the multiplication operation. +%0 = emitc.mul %arg0, %arg1 : (i32, i32) -> i32 +%1 = emitc.mul %arg2, %arg3 : (f32, f32) -> f32 +``` +```c++ +// Code emitted for the operations above. +int32_t v5 = v1 * v2; +float v6 = v3 * v4; +``` +""" +function mul(operand_0::Value, operand_1::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[operand_0, operand_1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`rem` + +With the `rem` operation the arithmetic operator % (remainder) can +be applied. + +# Example + +```mlir +// Custom form of the remainder operation. +%0 = emitc.rem %arg0, %arg1 : (i32, i32) -> i32 +``` +```c++ +// Code emitted for the operation above. +int32_t v5 = v1 % v2; +``` +""" +function rem(operand_0::Value, operand_1::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[operand_0, operand_1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.rem", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`return_` + +The `emitc.return` operation represents a return operation within a function. +The operation takes zero or exactly one operand and produces no results. +The operand number and type must match the signature of the function +that contains the operation. + +# Example + +```mlir +emitc.func @foo() : (i32) { + ... + emitc.return %0 : i32 +} +``` +""" +function return_(operand=nothing::Union{Nothing,Value}; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(operand) && push!(_operands, operand) + + return IR.create_operation( + "emitc.return", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sub` + +With the `sub` operation the arithmetic operator - (subtraction) can +be applied. + +# Example + +```mlir +// Custom form of the substraction operation. +%0 = emitc.sub %arg0, %arg1 : (i32, i32) -> i32 +%1 = emitc.sub %arg2, %arg3 : (!emitc.ptr, i32) -> !emitc.ptr +%2 = emitc.sub %arg4, %arg5 : (!emitc.ptr, !emitc.ptr) + -> !emitc.opaque<\"ptrdiff_t\"> +``` +```c++ +// Code emitted for the operations above. +int32_t v7 = v1 - v2; +float* v8 = v3 - v4; +ptrdiff_t v9 = v5 - v6; +``` +""" +function sub(lhs::Value, rhs::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.sub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`subscript` + +With the `subscript` operation the subscript operator `[]` can be applied +to variables or arguments of array, pointer and opaque type. + +# Example + +```mlir +%i = index.constant 1 +%j = index.constant 7 +%0 = emitc.subscript %arg0[%i, %j] : !emitc.array<4x8xf32>, index, index +%1 = emitc.subscript %arg1[%i] : !emitc.ptr, index +``` +""" +function subscript( + value::Value, indices::Vector{Value}; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[value, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.subscript", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`unary_minus` + +With the `unary_minus` operation the unary operator - (minus) can be +applied. + +# Example + +```mlir +%0 = emitc.unary_plus %arg0 : (i32) -> i32 +``` +```c++ +// Code emitted for the operation above. +int32_t v2 = -v1; +``` +""" +function unary_minus(operand_0::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[operand_0,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.unary_minus", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`unary_plus` + +With the `unary_plus` operation the unary operator + (plus) can be +applied. + +# Example + +```mlir +%0 = emitc.unary_plus %arg0 : (i32) -> i32 +``` +```c++ +// Code emitted for the operation above. +int32_t v2 = +v1; +``` +""" +function unary_plus(operand_0::Value; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[operand_0,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "emitc.unary_plus", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`variable` + +The `variable` operation produces an SSA value equal to some value +specified by an attribute. This can be used to form simple integer and +floating point variables, as well as more exotic things like tensor +variables. The `variable` operation also supports the EmitC opaque +attribute and the EmitC opaque type. If further supports the EmitC +pointer type, whereas folding is not supported. +The `variable` is emitted as a C/C++ local variable. + +# Example + +```mlir +// Integer variable +%0 = \"emitc.variable\"(){value = 42 : i32} : () -> i32 + +// Variable emitted as `int32_t* = NULL;` +%1 = \"emitc.variable\"() {value = #emitc.opaque<\"NULL\">} + : () -> !emitc.ptr> +``` + +Since folding is not supported, it can be used with pointers. +As an example, it is valid to create pointers to `variable` operations +by using `apply` operations and pass these to a `call` operation. +```mlir +%0 = \"emitc.variable\"() {value = 0 : i32} : () -> i32 +%1 = \"emitc.variable\"() {value = 0 : i32} : () -> i32 +%2 = emitc.apply \"&\"(%0) : (i32) -> !emitc.ptr +%3 = emitc.apply \"&\"(%1) : (i32) -> !emitc.ptr +emitc.call_opaque \"write\"(%2, %3) + : (!emitc.ptr, !emitc.ptr) -> () +``` +""" +function variable(; result_0::IR.Type, value, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "emitc.variable", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`verbatim` + +The `verbatim` operation produces no results and the value is emitted as is +followed by a line break (\'\\n\' character) during translation. + +Note: Use with caution. This operation can have arbitrary effects on the +semantics of the emitted code. Use semantically more meaningful operations +whenever possible. Additionally this op is *NOT* intended to be used to +inject large snippets of code. + +This operation can be used in situations where a more suitable operation is +not yet implemented in the dialect or where preprocessor directives +interfere with the structure of the code. One example of this is to declare +the linkage of external symbols to make the generated code usable in both C +and C++ contexts: + +```c++ +#ifdef __cplusplus +extern \"C\" { +#endif + +... + +#ifdef __cplusplus +} +#endif +``` +""" +function verbatim(; value, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "emitc.verbatim", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +\"yield\" terminates its parent EmitC op\'s region, optionally yielding +an SSA value. The semantics of how the values are yielded is defined by the +parent operation. +If \"yield\" has an operand, the operand must match the parent operation\'s +result. If the parent operation defines no values, then the \"emitc.yield\" +may be left out in the custom syntax and the builders will insert one +implicitly. Otherwise, it has to be present in the syntax to indicate which +value is yielded. +""" +function yield(result=nothing::Union{Nothing,Value}; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_operands, result) + + return IR.create_operation( + "emitc.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # emitc diff --git a/src/Dialects/19/Func.jl b/src/Dialects/19/Func.jl new file mode 100644 index 00000000..4acfb358 --- /dev/null +++ b/src/Dialects/19/Func.jl @@ -0,0 +1,231 @@ +module func + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`call_indirect` + +The `func.call_indirect` operation represents an indirect call to a value +of function type. The operands and result types of the call must match the +specified function type. + +Function values can be created with the +[`func.constant` operation](#funcconstant-constantop). + +# Example + +```mlir +%func = func.constant @my_func : (tensor<16xf32>, tensor<16xf32>) -> tensor<16xf32> +%result = func.call_indirect %func(%0, %1) : (tensor<16xf32>, tensor<16xf32>) -> tensor<16xf32> +``` +""" +function call_indirect( + callee::Value, + callee_operands::Vector{Value}; + results::Vector{IR.Type}, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[callee, callee_operands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "func.call_indirect", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`call` + +The `func.call` operation represents a direct call to a function that is +within the same symbol scope as the call. The operands and result types of +the call must match the specified function type. The callee is encoded as a +symbol reference attribute named \"callee\". + +# Example + +```mlir +%2 = func.call @my_add(%0, %1) : (f32, f32) -> f32 +``` +""" +function call( + operands::Vector{Value}; result_0::Vector{IR.Type}, callee, location=Location() +) + _results = IR.Type[result_0...,] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("callee", callee),] + + return IR.create_operation( + "func.call", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`constant` + +The `func.constant` operation produces an SSA value from a symbol reference +to a `func.func` operation + +# Example + +```mlir +// Reference to function @myfn. +%2 = func.constant @myfn : (tensor<16xf32>, f32) -> tensor<16xf32> + +// Equivalent generic forms +%2 = \"func.constant\"() { value = @myfn } : () -> ((tensor<16xf32>, f32) -> tensor<16xf32>) +``` + +MLIR does not allow direct references to functions in SSA operands because +the compiler is multithreaded, and disallowing SSA values to directly +reference a function simplifies this +([rationale](../Rationale/Rationale.md#multithreading-the-compiler)). +""" +function constant(; result_0::IR.Type, value, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "func.constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`func_` + +Operations within the function cannot implicitly capture values defined +outside of the function, i.e. Functions are `IsolatedFromAbove`. All +external references must use function arguments or attributes that establish +a symbolic connection (e.g. symbols referenced by name via a string +attribute like SymbolRefAttr). An external function declaration (used when +referring to a function declared in some other module) has no body. While +the MLIR textual form provides a nice inline syntax for function arguments, +they are internally represented as “block arguments” to the first block in +the region. + +Only dialect attribute names may be specified in the attribute dictionaries +for function arguments, results, or the function itself. + +# Example + +```mlir +// External function definitions. +func.func private @abort() +func.func private @scribble(i32, i64, memref) -> f64 + +// A function that returns its argument twice: +func.func @count(%x: i64) -> (i64, i64) + attributes {fruit: \"banana\"} { + return %x, %x: i64, i64 +} + +// A function with an argument attribute +func.func private @example_fn_arg(%x: i32 {swift.self = unit}) + +// A function with a result attribute +func.func private @example_fn_result() -> (f64 {dialectName.attrName = 0 : i64}) + +// A function with an attribute +func.func private @example_fn_attr() attributes {dialectName.attrName = false} +``` +""" +function func_(; + sym_name, + function_type, + sym_visibility=nothing, + arg_attrs=nothing, + res_attrs=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("function_type", function_type) + ] + !isnothing(sym_visibility) && + push!(_attributes, namedattribute("sym_visibility", sym_visibility)) + !isnothing(arg_attrs) && push!(_attributes, namedattribute("arg_attrs", arg_attrs)) + !isnothing(res_attrs) && push!(_attributes, namedattribute("res_attrs", res_attrs)) + + return IR.create_operation( + "func.func", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`return_` + +The `func.return` operation represents a return operation within a function. +The operation takes variable number of operands and produces no results. +The operand number and types must match the signature of the function +that contains the operation. + +# Example + +```mlir +func.func @foo() : (i32, f8) { + ... + return %0, %1 : i32, f8 +} +``` +""" +function return_(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "func.return", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # func diff --git a/src/Dialects/19/GPU.jl b/src/Dialects/19/GPU.jl new file mode 100644 index 00000000..552d67d1 --- /dev/null +++ b/src/Dialects/19/GPU.jl @@ -0,0 +1,3208 @@ +module gpu + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`all_reduce` + +The `all_reduce` op reduces the value of every work item across a local +workgroup. The result is equal for all work items of a workgroup. + +For example, both + +```mlir +%1 = gpu.all_reduce add %0 {} : (f32) -> (f32) +%2 = gpu.all_reduce %0 { +^bb(%lhs : f32, %rhs : f32): + %sum = arith.addf %lhs, %rhs : f32 + \"gpu.yield\"(%sum) : (f32) -> () +} : (f32) -> (f32) +``` + +compute the sum of each work item\'s %0 value. The first version specifies +the accumulation as operation, whereas the second version specifies the +accumulation as code region. The reduction operation must be one of: +* Integer types: `add`, `mul`, `minui`, `minsi`, `maxui`, `maxsi`, `and`, + `or`, `xor` +* Floating point types: `add`, `mul`, `minnumf`, `maxnumf`, `minimumf`, + `maximumf` + +If `uniform` flag is set either none or all work items of a workgroup +need to execute this op in convergence. +""" +function all_reduce( + value::Value; + result=nothing::Union{Nothing,IR.Type}, + op=nothing, + uniform=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(op) && push!(_attributes, namedattribute("op", op)) + !isnothing(uniform) && push!(_attributes, namedattribute("uniform", uniform)) + + return IR.create_operation( + "gpu.all_reduce", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`alloc` + +The `gpu.alloc` operation allocates a region of memory on the GPU. It is +similar to the `memref.alloc` op, but supports asynchronous GPU execution. + +The op does not execute before all async dependencies have finished +executing. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it also returns a !gpu.async.token. + +If the `host_shared` keyword is present, the memory will be allocated in a +memory accessible both on host and on device. + +# Example + +```mlir +%memref, %token = gpu.alloc async [%dep] host_shared (%width) : memref<64x?xf32, 1> +``` +""" +function alloc( + asyncDependencies::Vector{Value}, + dynamicSizes::Vector{Value}, + symbolOperands::Vector{Value}; + memref::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + hostShared=nothing, + location=Location(), +) + _results = IR.Type[memref,] + _operands = Value[asyncDependencies..., dynamicSizes..., symbolOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + push!( + _attributes, + operandsegmentsizes([ + length(asyncDependencies), length(dynamicSizes), length(symbolOperands) + ]), + ) + !isnothing(asyncToken) && push!(_results, asyncToken) + !isnothing(hostShared) && push!(_attributes, namedattribute("hostShared", hostShared)) + + return IR.create_operation( + "gpu.alloc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`barrier` + +The \"barrier\" op synchronizes all work items of a workgroup. It is used +to coordinate communication between the work items of the workgroup. + +```mlir +gpu.barrier +``` + +waits until all work items in the workgroup have reached this point +and all memory accesses made by these work items prior to the op are +visible to all work items in the workgroup. Data hazards between work items +accessing the same memory can be avoided by synchronizing work items +in-between these accesses. + +Either none or all work items of a workgroup need to execute this op +in convergence. +""" +function barrier(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "gpu.barrier", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`binary` + +GPU binaries provide a semantic mechanism for storing GPU objects, +e.g. the result of compiling a GPU module to an object file. + +This operation has 3 arguments: + - The name of the binary. + - An optional attribute implementing the offloading LLVM translation interface. + - An array of GPU object attributes. + +During translation, the offloading attribute will be called for translating +GPU `binary` and `launch_func` operations. The default offloading handler is: +`#gpu.select_object`, this handler selects the first object from the array +and embeds it as a string. + +Examples: +``` + // Selects the first object. + gpu.binary @myobject [#gpu.object<...>, #gpu.object<...>] + // Uses the `#foo.my_handler` for handling the binary during translation. + gpu.binary @myobject <#foo.my_handler> [#gpu.object<...>, #gpu.object<...>] + // Selects the object with the `#rocdl.target` target attribute. + gpu.binary @myobject <#gpu.select_object<#rocdl.target>> [#gpu.object<...>, #gpu.object<#rocdl.target, ...>] +``` +""" +function binary(; sym_name, offloadingHandler=nothing, objects, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("objects", objects) + ] + !isnothing(offloadingHandler) && + push!(_attributes, namedattribute("offloadingHandler", offloadingHandler)) + + return IR.create_operation( + "gpu.binary", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`block_dim` + +Returns the number of threads in the thread block (aka the block size) along +the x, y, or z `dimension`. + +# Example + +```mlir +%bDimX = gpu.block_dim x +``` + +If `known_block_size` is set on an this operation\'s enclosing `gpu.func`, +or `gpu.known_block_size` is set on an enclosing `FunctionOpInterface` +implementor, or if the enclosing `gpu.launch` specifies a constant size for +`dimension`\'s blocks, these contextual facts may be used to infer that this +operation has a constant value, though such a transformation will not be +performed by canonicalization or the default constant folder. Executions which +cause that constant-value assumption to be false incur undefined behavior. + +If `upper_bound` is set, executions where the bblock size along `dimension` +exceeds `upper_bound` cause undefined behavior. + +There is an implicit upper bound of `kMaxDim` (currently uint32_t::max). +""" +function block_dim(; + result_0=nothing::Union{Nothing,IR.Type}, + dimension, + upper_bound=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimension", dimension),] + !isnothing(result_0) && push!(_results, result_0) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.block_dim", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`block_id` + +Returns the block id, i.e. the index of the current block within the grid +along the x, y, or z `dimension`. + +# Example + +```mlir +%bIdY = gpu.block_id y +``` + +If `upper_bound` is set, or if one can be inferred from `known_grid_size`-type +annotations in context, executions where the block index in `dimension` would +be greater than or equal to that bound cause undefined behavior. `upper_bound` +takes priority over bounds inferrable from context. + +There is an implicit upper bound of `kMaxDim` (currently uint32_t::max). +""" +function block_id(; + result_0=nothing::Union{Nothing,IR.Type}, + dimension, + upper_bound=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimension", dimension),] + !isnothing(result_0) && push!(_results, result_0) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.block_id", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cluster_block_id` + +Returns the block id within the cluster along the x, y, or z `dimension`. + +# Example + +```mlir +%cBlockIdY = gpu.cluster_block_id y +``` + +If `upper_bound` is set, then executing (a lowering of) this operation in an +environment where the number of thread blocks per cluster along `dimension` +is greater than `upper_bound` causes undefined behavior. + +There is an implicit upper bound of `kMaxClusterDim` (currently 8). +""" +function cluster_block_id(; + result_0=nothing::Union{Nothing,IR.Type}, + dimension, + upper_bound=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimension", dimension),] + !isnothing(result_0) && push!(_results, result_0) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.cluster_block_id", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cluster_dim_blocks` + +Returns the number of thread blocks in the cluster along +the x, y, or z `dimension`. + +# Example + +```mlir +%cDimBlocksX = gpu.cluster_dim_blocks x +``` + +If `upper_bound` is set, then executing (a lowering of) this operation in an +environment where the thread blocks per cluster is greater than `upper_bound` +causes undefined behavior. + +There is an implicit upper bound of `kMaxClusterDim` (currently 8). +""" +function cluster_dim_blocks(; + result_0=nothing::Union{Nothing,IR.Type}, + dimension, + upper_bound=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimension", dimension),] + !isnothing(result_0) && push!(_results, result_0) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.cluster_dim_blocks", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cluster_dim` + +Returns the number of cluster identifiers per grid along +the x, y, or z `dimension`. + +# Example + +```mlir +%cDimX = gpu.cluster_dim x +``` + +If `upper_bound` is set, then executing (a lowering of) this operation in an +environment where the clusters per grid is greater than `upper_bound` causes +undefined behavior. + +There is an implicit upper bound of `kMaxDim` (currently uint32_t::max). +""" +function cluster_dim(; + result_0=nothing::Union{Nothing,IR.Type}, + dimension, + upper_bound=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimension", dimension),] + !isnothing(result_0) && push!(_results, result_0) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.cluster_dim", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cluster_id` + +Returns the cluster id, i.e. the index of the current cluster within the +grid along the x, y, or z `dimension`. + +# Example + +```mlir +%cIdY = gpu.cluster_id y +``` + +If `upper_bound` is set, then executing (a lowering of) this operation in an +environment where the number of clusters in the grid along `dimension` is +greater than `upper_bound` causes undefined behavior. + +There is an implicit upper bound of `kMaxDim` (currently uint32_t::max). +""" +function cluster_id(; + result_0=nothing::Union{Nothing,IR.Type}, + dimension, + upper_bound=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimension", dimension),] + !isnothing(result_0) && push!(_results, result_0) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.cluster_id", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`create_2to4_spmat` + +The `gpu.create_2to4_spmat` operation initializes a sparse matrix in dense +format with 2:4 sparsity. +The buffers must already be copied from the host to the device prior to +using this operation. The operation returns a handle to the sparse +matrix descriptor. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +# Example + +```mlir +%spmat, %token = gpu.create_2to4_spmat async [%dep] {PRUNE_AND_CHECK} %rows, %cols, %mem: memref +``` +""" +function create_2to4_spmat( + asyncDependencies::Vector{Value}, + rows::Value, + cols::Value, + memref::Value; + spMat::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + pruneFlag=nothing, + location=Location(), +) + _results = IR.Type[spMat,] + _operands = Value[asyncDependencies..., rows, cols, memref] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + !isnothing(pruneFlag) && push!(_attributes, namedattribute("pruneFlag", pruneFlag)) + + return IR.create_operation( + "gpu.create_2to4_spmat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_bsr` + +The `gpu.create_bsr` operation initializes a sparse matrix in BSR format +with the given sizes for the matrix and blocks from the given position, +index, and values buffers. The buffers must already be copied from the +host to the device prior to using this operation. The operation returns +a handle to the sparse matrix descriptor. + +The BSR format is similar to CSR, where the column indices represent +two-dimensional blocks instead of a single matrix entry. Note that this +operation (currently) only supports storage with **square** blocks, +i.e., `rBlockSize == cBlockSize`. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +# Example + +```mlir +%spmat, %token = gpu.create_bsr async [%dep] + %brows, %bcols, %bnnz, %rBlockSize, %cBlockSize, + %bRowPos, %bColIdxs, %values : memref, memref, memref +``` +""" +function create_bsr( + asyncDependencies::Vector{Value}, + brows::Value, + bcols::Value, + bnnz::Value, + rBlockSize::Value, + cBlockSize::Value, + bRowPos::Value, + bColIdxs::Value, + values::Value; + spmat::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[spmat,] + _operands = Value[ + asyncDependencies..., + brows, + bcols, + bnnz, + rBlockSize, + cBlockSize, + bRowPos, + bColIdxs, + values, + ] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.create_bsr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_coo_aos` + +The `gpu.create_coo_aos` operation initializes a sparse matrix in COO format +with the given sizes from the given index and values buffers. The buffers +must already be copied from the host to the device prior to using this +operation. The operation returns a handle to the sparse matrix descriptor. +Unlike the default `gpu.create_coo` operation, this operation builds the +COO format from a single index buffer in AoS format (note that this +feature has been deprecated in cuSparse 11.2). + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +# Example + +```mlir +%spmat, %token = gpu.create_coo_aos async [%dep] %rows, %cols, %nnz, %idxs, + %values : memref, memref +``` +""" +function create_coo_aos( + asyncDependencies::Vector{Value}, + rows::Value, + cols::Value, + nnz::Value, + idxs::Value, + values::Value; + spmat::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[spmat,] + _operands = Value[asyncDependencies..., rows, cols, nnz, idxs, values] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.create_coo_aos", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_coo` + +The `gpu.create_coo` operation initializes a sparse matrix in COO format +with the given sizes from the given index and values buffers. The buffers +must already be copied from the host to the device prior to using this +operation. The operation returns a handle to the sparse matrix descriptor. +Note that this operation builds the COO in SoA format. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +# Example + +```mlir +%spmat, %token = gpu.create_coo async [%dep] %rows, %cols, %nnz, %rowIdx, + %colIdx, %values : memref, memref, memref +``` +""" +function create_coo( + asyncDependencies::Vector{Value}, + rows::Value, + cols::Value, + nnz::Value, + rowIdxs::Value, + colIdxs::Value, + values::Value; + spmat::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[spmat,] + _operands = Value[asyncDependencies..., rows, cols, nnz, rowIdxs, colIdxs, values] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.create_coo", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_csc` + +The `gpu.create_csc` operation initializes a sparse matrix in CSC format +with the given sizes from the given position, index, and values buffers. +The buffers must already be copied from the host to the device prior to +using this operation. The operation returns a handle to the sparse +matrix descriptor. + +The CSC format has exactly the same memory layout as its transpose +in CSR format (and vice versa). + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +# Example + +```mlir +%spmat, %token = gpu.create_csc async [%dep] %rows, %cols, %nnz, %colPos, + %rowIdx, %values : memref, memref, memref +``` +""" +function create_csc( + asyncDependencies::Vector{Value}, + rows::Value, + cols::Value, + nnz::Value, + colPos::Value, + rowIdxs::Value, + values::Value; + spmat::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[spmat,] + _operands = Value[asyncDependencies..., rows, cols, nnz, colPos, rowIdxs, values] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.create_csc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_csr` + +The `gpu.create_csr` operation initializes a sparse matrix in CSR format +with the given sizes from the given position, index, and values buffers. +The buffers must already be copied from the host to the device prior to +using this operation. The operation returns a handle to the sparse +matrix descriptor. + +The CSR format has exactly the same memory layout as its transpose +in CSC format (and vice versa). + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +# Example + +```mlir +%spmat, %token = gpu.create_csr async [%dep] %rows, %cols, %nnz, %rowPos, + %colIdx, %values : memref, memref, memref +``` +""" +function create_csr( + asyncDependencies::Vector{Value}, + rows::Value, + cols::Value, + nnz::Value, + rowPos::Value, + colIdxs::Value, + values::Value; + spmat::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[spmat,] + _operands = Value[asyncDependencies..., rows, cols, nnz, rowPos, colIdxs, values] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.create_csr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_dn_tensor` + +The `gpu.create_dn_tensor` operation initializes a dense tensor from +the given values buffer and sizes. The buffer must already be copied +from the host to the device prior to using this operation. The +operation returns a handle to the dense tensor descriptor. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +# Example + +```mlir +%dmat, %token = gpu.create_dn_tensor async [%dep] %mem, %dims : index, index into memref +``` +""" +function create_dn_tensor( + asyncDependencies::Vector{Value}, + memref::Value, + dims::Vector{Value}; + dnTensor::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[dnTensor,] + _operands = Value[asyncDependencies..., memref, dims...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(asyncDependencies), 1, length(dims)])) + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.create_dn_tensor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`dealloc` + +The `gpu.dealloc` operation frees the region of memory referenced by a +memref which was originally created by the `gpu.alloc` operation. It is +similar to the `memref.dealloc` op, but supports asynchronous GPU execution. + +The op does not execute before all async dependencies have finished +executing. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token. + +# Example + +```mlir +%token = gpu.dealloc async [%dep] %memref : memref<8x64xf32, 1> +``` +""" +function dealloc( + asyncDependencies::Vector{Value}, + memref::Value; + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies..., memref] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.dealloc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`destroy_dn_tensor` + +The `gpu.destroy_dn_tensor` operation releases all resources of a dense +tensor represented by a handle that was previously created by a +`gpu.create_dn_tensor` operation. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +# Example + +```mlir +%token = gpu.destroy_dn_tensor async [%dep] %dnTensor +``` +""" +function destroy_dn_tensor( + asyncDependencies::Vector{Value}, + dnTensor::Value; + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies..., dnTensor] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.destroy_dn_tensor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`destroy_sp_mat` + +The `gpu.destroy_sp_mat` operation releases all resources of a sparse +matrix represented by a handle that was previously created by a +one of the sparse matrix creation operations. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +# Example + +```mlir +%token = gpu.destroy_sp_mat async [%dep] %spmat +``` +""" +function destroy_sp_mat( + asyncDependencies::Vector{Value}, + spmat::Value; + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies..., spmat] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.destroy_sp_mat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`dynamic_shared_memory` + +This operation provides a memref pointer to the start of dynamic shared +memory, often referred to as workgroup memory. It\'s important to note that +this dynamic shared memory needs to be allocated at kernel launch. One can +conveniently utilize `the dynamic_shared_memory_size` parameter of +`gpu.launch` for this purpose. + +Examples: +```mlir +%0 = gpu.dynamic.shared.memory : memref> +%1 = memref.view %0[%c8192][] : memref> + to memref<32x64xf32, #gpu.address_space> +%2 = memref.view %0[%c16384][] : memref> + to memref<32x64xf32, #gpu.address_space> +``` +""" +function dynamic_shared_memory(; resultMemref::IR.Type, location=Location()) + _results = IR.Type[resultMemref,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "gpu.dynamic_shared_memory", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`func` + +Defines a function that can be executed on a GPU. This supports memory +attribution and its body has a particular execution model. + +GPU functions are either kernels (as indicated by the `kernel` attribute) or +regular functions. The former can be launched from the host side, while the +latter are device side only. + +The memory attribution defines SSA values that correspond to memory buffers +allocated in the memory hierarchy of the GPU (see below). + +The operation has one attached region that corresponds to the body of the +function. The region arguments consist of the function arguments without +modification, followed by buffers defined in memory annotations. The body of +a GPU function, when launched, is executed by multiple work items. There are +no guarantees on the order in which work items execute, or on the connection +between them. In particular, work items are not necessarily executed in +lock-step. Synchronization ops such as \"gpu.barrier\" should be used to +coordinate work items. Declarations of GPU functions, i.e. not having the +body region, are not supported. + +A function may optionally be annotated with the block and/or grid sizes +that will be used when it is launched using the `known_block_size` and +`known_grid_size` attributes, respectively. If set, these attributes must +be arrays of three 32-bit integers giving the x, y, and z launch dimensions. +Launching a kernel that has these annotations, or that calls a function with +these annotations, using a block size or grid size other than what is specified +is undefined behavior. These attributes may be set on non-`gpu.func` functions +by using `gpu.known_block_size` or `gpu.known_grid_size`, but this carries +the risk that they will de discarded. + +# Syntax + +``` +op ::= `gpu.func` symbol-ref-id `(` argument-list `)` (`->` +function-result-list)? + memory-attribution `kernel`? function-attributes? region + +memory-attribution ::= (`workgroup` `(` ssa-id-and-type-list `)`)? + (`private` `(` ssa-id-and-type-list `)`)? +``` + +# Example + +```mlir +gpu.func @foo(%arg0: index) + workgroup(%workgroup: memref<32xf32, 3>) + private(%private: memref<1xf32, 5>) + kernel + attributes {qux: \"quux\"} { + gpu.return +} +``` + +The generic form illustrates the concept + +```mlir +\"gpu.func\"(%arg: index) {sym_name: \"foo\", kernel, qux: \"quux\"} ({ +^bb0(%arg0: index, %workgroup: memref<32xf32, 3>, + %private: memref<1xf32, 5>): + \"gpu.return\"() : () -> () +}) : (index) -> () +``` + +Note the non-default memory spaces used in memref types in memory +attribution. +""" +function func(; + function_type, + arg_attrs=nothing, + res_attrs=nothing, + workgroup_attrib_attrs=nothing, + private_attrib_attrs=nothing, + known_block_size=nothing, + known_grid_size=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("function_type", function_type),] + !isnothing(arg_attrs) && push!(_attributes, namedattribute("arg_attrs", arg_attrs)) + !isnothing(res_attrs) && push!(_attributes, namedattribute("res_attrs", res_attrs)) + !isnothing(workgroup_attrib_attrs) && + push!(_attributes, namedattribute("workgroup_attrib_attrs", workgroup_attrib_attrs)) + !isnothing(private_attrib_attrs) && + push!(_attributes, namedattribute("private_attrib_attrs", private_attrib_attrs)) + !isnothing(known_block_size) && + push!(_attributes, namedattribute("known_block_size", known_block_size)) + !isnothing(known_grid_size) && + push!(_attributes, namedattribute("known_grid_size", known_grid_size)) + + return IR.create_operation( + "gpu.func", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`module_` + +GPU module contains code that is intended to be run on a GPU. A host device +can launch this code through a gpu.launc_func that creates a fully +qualified symbol through the gpu.module\'s symbol and a gpu.func symbol +contained in the gpu.module. + +The module\'s top-level scope is modeled by a single region with a single +block. GPU modules are required to have a name that is used for symbol +resolution by the gpu.launch_func operation. + +Using an op with a region to define a GPU module enables \"embedding\" GPU +modules with SIMT execution models in other dialects in a clean manner and +allows filtering of code regions to execute passes on only code intended to +or not intended to be run on the separate device. + +Modules can contain zero or more target attributes. These attributes encode +how to transform modules into binary strings and are used by the +`gpu-module-to-binary` pass to transform modules into GPU binaries. + +Modules can contain an optional `OffloadingTranslationAttr` attribute. This +attribute will be used during the `gpu-module-to-binary` pass to specify the +`OffloadingTranslationAttr` used when creating the `gpu.binary` operation. + +``` +gpu.module @symbol_name { + gpu.func {} + ... + gpu.module_end +} +// Module with offloading handler and target attributes. +gpu.module @symbol_name2 <#gpu.select_object<1>> [ + #nvvm.target, + #rocdl.target] { + gpu.func {} + ... + gpu.module_end +} +``` +""" +function module_(; + sym_name, + targets=nothing, + offloadingHandler=nothing, + bodyRegion::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[bodyRegion,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("sym_name", sym_name),] + !isnothing(targets) && push!(_attributes, namedattribute("targets", targets)) + !isnothing(offloadingHandler) && + push!(_attributes, namedattribute("offloadingHandler", offloadingHandler)) + + return IR.create_operation( + "gpu.module", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`global_id` + +Returns the unique global workitem/thread id, i.e., the unique index of the +current workitem/thread within all workgroups / grid along the x, y, or z +`dimension`. + +# Example + +```mlir +%gidX = gpu.global_id x +%gidX = gpu.global_id x upper_bound 65536 +``` + +The `upper_bound` attribute defines an upper bound analogously to the ones on +`thread_id` and `block_id`. If one is not set, the bound may be inferred from +a combination of `known_block_size` and `known_grid_size`-type annotations. +""" +function global_id(; + result_0=nothing::Union{Nothing,IR.Type}, + dimension, + upper_bound=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimension", dimension),] + !isnothing(result_0) && push!(_results, result_0) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.global_id", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`grid_dim` + +Returns the number of thread blocks in the grid along the x, y, or z +`dimension`. + +# Example + +```mlir +%gDimZ = gpu.grid_dim z +``` + + +If `known_grid_size` is set on an this operation\'s enclosing `gpu.func`, +or `gpu.known_grid_size` is set on an enclosing `FunctionOpInterface` +implementor, or if the enclosing `gpu.launch` specifies a constant size for +`dimension`\'s grid length, these contextual facts may be used to infer that this +operation has a constant value, though such a transformation will not be +performed by canonicalization or the default constant folder. Executions which +cause that constant-value assumption to be false incur undefined behavior. + +If `upper_bound` is set, executions where the grid size in `dimension` would +exceed `upper_bound` cause undefined behavior. + +There is an implicit upper bound of `kMaxDim` (currently uint32_t::max). +""" +function grid_dim(; + result_0=nothing::Union{Nothing,IR.Type}, + dimension, + upper_bound=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimension", dimension),] + !isnothing(result_0) && push!(_results, result_0) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.grid_dim", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`host_register` + +This op maps the provided host buffer into the device address space. + +This operation may not be supported in every environment, there is not yet a +way to check at runtime whether this feature is supported. + +Writes from the host are guaranteed to be visible to device kernels that are +launched afterwards. Writes from the device are guaranteed to be visible on +the host after synchronizing with the device kernel completion. +""" +function host_register(value::Value; location=Location()) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "gpu.host_register", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`host_unregister` + +This op unmaps the provided host buffer from the device address space. + +This operation may not be supported in every environment, there is not yet a + way to check at runtime whether this feature is supported. +""" +function host_unregister(value::Value; location=Location()) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "gpu.host_unregister", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`lane_id` + +Returns the lane id within the subgroup (warp/wave). + +# Example +```mlir +%laneId = gpu.lane_id +``` + +If `upper_bound` is set, executions with more than `upper_bound` lanes per +subgroup cause undefined behavior. In the abscence of `upper_bound`, +the lane id is still assumed to be non-negative and less than the +target-independent `kMaxSubgroupSize` (currently 128). +""" +function lane_id(; + result=nothing::Union{Nothing,IR.Type}, upper_bound=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.lane_id", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`launch_func` + +Launch a kernel function on the specified grid of thread blocks. +`gpu.launch` operations are lowered to `gpu.launch_func` operations by +outlining the kernel body into a function in a dedicated module, which +reflects the separate compilation process. The kernel function is required +to have the `gpu.kernel` attribute. The module containing the kernel +function is required to be a gpu.module. And finally, the module containing +the kernel module (which thus cannot be the top-level module) is required +to have the `gpu.container_module` attribute. The `gpu.launch_func` +operation has a symbol attribute named `kernel` to identify the fully +specified kernel function to launch (both the gpu.module and func). + +The `gpu.launch_func` supports async dependencies: the kernel does not start +executing until the ops producing those async dependencies have completed. + +By the default, the host implicitly blocks until kernel execution has +completed. If the `async` keyword is present, the host does not block but +instead a `!gpu.async.token` is returned. Other async GPU ops can take this +token as dependency. + +The operation requires at least the grid and block sizes along the x,y,z +dimensions as arguments. When a lower-dimensional kernel is required, +unused sizes must be explicitly set to `1`. + +The remaining operands are optional. The first optional operand corresponds +to the amount of dynamic shared memory a kernel\'s workgroup should be +allocated; when this operand is not present, a zero size is assumed. + +The remaining operands if present are passed as arguments to the kernel +function. + +The `gpu.launch_func` also supports kernel launching with clusters if +supported by the target architecture. The cluster size can be set by +`clusterSizeX`, `clusterSizeY`, and `clusterSizeZ` arguments. When these +arguments are present, the Op launches a kernel that clusters the given +thread blocks. This feature is exclusive to certain architectures. + +# Example + +```mlir +module attributes {gpu.container_module} { + + // This module creates a separate compilation unit for the GPU compiler. + gpu.module @kernels { + func.func @kernel_1(%arg0 : f32, %arg1 : memref) + attributes { nvvm.kernel = true } { + + // Operations that produce block/thread IDs and dimensions are + // injected when outlining the `gpu.launch` body to a function called + // by `gpu.launch_func`. + %tIdX = gpu.thread_id x + %tIdY = gpu.thread_id y + %tIdZ = gpu.thread_id z + + %bDimX = gpu.block_dim x + %bDimY = gpu.block_dim y + %bDimZ = gpu.block_dim z + + %bIdX = gpu.block_id x + %bIdY = gpu.block_id y + %bIdZ = gpu.block_id z + + %gDimX = gpu.grid_dim x + %gDimY = gpu.grid_dim y + %gDimZ = gpu.grid_dim z + + // (Optional) Cluster size only for support architectures + %cIdX = gpu.cluster_id x + %cIdY = gpu.cluster_id y + %cIdZ = gpu.cluster_id z + + %cDimX = gpu.cluster_dim x + %cDimY = gpu.cluster_dim y + %cDimZ = gpu.cluster_dim z + + \"some_op\"(%bx, %tx) : (index, index) -> () + %42 = load %arg1[%bx] : memref + } + } + + %t0 = gpu.wait async + gpu.launch_func + async // (Optional) Don\'t block host, return token. + [%t0] // (Optional) Execute only after %t0 has completed. + @kernels::@kernel_1 // Kernel function. + clusters in (%cst, %cst, %cst) // (Optional) Cluster size only for support architectures. + blocks in (%cst, %cst, %cst) // Grid size. + threads in (%cst, %cst, %cst) // Block size. + dynamic_shared_memory_size %s // (Optional) Amount of dynamic shared + // memory to allocate for a workgroup. + args(%arg0 : f32, // (Optional) Kernel arguments. + %arg1 : memref) +} +``` +""" +function launch_func( + asyncDependencies::Vector{Value}, + gridSizeX::Value, + gridSizeY::Value, + gridSizeZ::Value, + blockSizeX::Value, + blockSizeY::Value, + blockSizeZ::Value, + clusterSizeX=nothing::Union{Nothing,Value}; + clusterSizeY=nothing::Union{Nothing,Value}, + clusterSizeZ=nothing::Union{Nothing,Value}, + dynamicSharedMemorySize=nothing::Union{Nothing,Value}, + kernelOperands::Vector{Value}, + asyncObject=nothing::Union{Nothing,Value}, + asyncToken=nothing::Union{Nothing,IR.Type}, + kernel, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ + asyncDependencies..., + gridSizeX, + gridSizeY, + gridSizeZ, + blockSizeX, + blockSizeY, + blockSizeZ, + kernelOperands..., + ] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("kernel", kernel),] + !isnothing(clusterSizeX) && push!(_operands, clusterSizeX) + !isnothing(clusterSizeY) && push!(_operands, clusterSizeY) + !isnothing(clusterSizeZ) && push!(_operands, clusterSizeZ) + !isnothing(dynamicSharedMemorySize) && push!(_operands, dynamicSharedMemorySize) + !isnothing(asyncObject) && push!(_operands, asyncObject) + push!( + _attributes, + operandsegmentsizes([ + length(asyncDependencies), + 1, + 1, + 1, + 1, + 1, + 1, + isnothing(clusterSizeX) ? 0 : 1, + isnothing(clusterSizeY) ? 0 : 1, + isnothing(clusterSizeZ) ? 0 : 1, + isnothing(dynamicSharedMemorySize) ? 0 : 1, + length(kernelOperands), + isnothing(asyncObject) ? 0 : 1, + ]), + ) + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.launch_func", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`launch` + +Launch a kernel on the specified grid of thread blocks. The body of the +kernel is defined by the single region that this operation contains. The +operation takes an optional list of async dependencies followed by six +operands and an optional operand. + +The `async` keyword indicates the kernel should be launched asynchronously; +the operation returns a new !gpu.async.token when the keyword is specified. +The kernel launched does not start executing until the ops producing its +async dependencies (optional operands) have completed. + +The first three operands (following any async dependencies) are grid sizes +along the x,y,z dimensions and the following three are block sizes along the +x,y,z dimensions. When a lower-dimensional kernel is required, unused sizes +must be explicitly set to `1`. The last operand is optional and corresponds +to the amount of dynamic shared memory a kernel\'s workgroup should be +allocated; when this operand is not present, a zero size is assumed. + +The body region has at least _twelve_ arguments, or _eighteen_ if cluster +dimensions are present, grouped as follows: + +- three optional arguments that contain cluster identifiers along x,y,z + dimensions; +- three arguments that contain block identifiers along x,y,z dimensions; +- three arguments that contain thread identifiers along x,y,z dimensions; +- operands of the `gpu.launch` operation as is (i.e. the operands for + grid and block sizes). +- a variadic number of Workgroup memory attributions. +- a variadic number of Private memory attributions. + +# Syntax + +``` +operation ::= `gpu.launch` (`async` (`[` ssa-id-list `]`)? )? + ( `clusters` `(` ssa-id-list `)` `in` ssa-reassignment )? + `blocks` `(` ssa-id-list `)` `in` ssa-reassignment + `threads` `(` ssa-id-list `)` `in` ssa-reassignment + (dynamic_shared_memory_size ssa-use)? + memory-attribution + region attr-dict? +ssa-reassignment ::= `(` ssa-id `=` ssa-use (`,` ssa-id `=` ssa-use)* `)` +memory-attribution ::= (`workgroup` `(` ssa-id-and-type-list `)`)? + (`private` `(` ssa-id-and-type-list `)`)? +``` + +# Example + +```mlir +gpu.launch blocks(%bx, %by, %bz) in (%sz_bx = %0, %sz_by = %1, %sz_bz = %2) + threads(%tx, %ty, %tz) in (%sz_tx = %3, %sz_ty = %4, %sz_tz = %5) { + // Block and thread identifiers, as well as block/grid sizes are + // immediately usable inside body region. + \"some_op\"(%bx, %tx) : (index, index) -> () + // Assuming %val1 is defined outside the gpu.launch region. + %42 = load %val1[%bx] : memref +} + +// Generic syntax explains how the pretty syntax maps to the IR structure. +\"gpu.launch\"(%cst, %cst, %c1, // Grid sizes. + %cst, %c1, %c1) // Block sizes. + + {/*attributes*/} + // All sizes and identifiers have \"index\" size. + : (index, index, index, index, index, index) -> () { +// The operation passes block and thread identifiers, followed by grid and +// block sizes. +^bb0(%bx : index, %by : index, %bz : index, + %tx : index, %ty : index, %tz : index, + %num_bx : index, %num_by : index, %num_bz : index, + %num_tx : index, %num_ty : index, %num_tz : index) + \"some_op\"(%bx, %tx) : (index, index) -> () + %3 = \"memref.load\"(%val1, %bx) : (memref, index) -> f32 +} + +// Launch with memory attributions. +gpu.launch blocks(%bx, %by, %bz) in (%sz_bx = %0, %sz_by = %1, %sz_bz = %2) + threads(%tx, %ty, %tz) in (%sz_tx = %3, %sz_ty = %4, %sz_tz = %5) + workgroup(%workgroup: memref<32xf32, 3>) + private(%private: memref<1xf32, 5>) { + // Block and thread identifiers, as well as block/grid sizes are + // immediately usable inside body region. + \"some_op\"(%bx, %tx) : (index, index) -> () + // Assuming %val1 is defined outside the gpu.launch region. + %42 = load %workgroup[%bx] : memref<32xf32, 3> +} + +// Launch with clusters. +gpu.launch clusters(%cx, %cy, %cz) in (%sz_cx = %0, %sz_cy = %1, %sz_cz = %2) + blocks(%bx, %by, %bz) in (%sz_bx = %3, %sz_by = %4, %sz_bz = %5) + threads(%tx, %ty, %tz) in (%sz_tx = %6, %sz_ty = %7, %sz_tz = %8) +{ + // Cluster, block and thread identifiers, as well as cluster/block/grid + // sizes are immediately usable inside body region. + \"some_op\"(%cx, %bx, %tx) : (index, index, index) -> () +} +``` + +Rationale: using operation/block arguments gives analyses a clear way of +understanding that a value has additional semantics (e.g., we will need to +know what value corresponds to threadIdx.x for coalescing). We can recover +these properties by analyzing the operations producing values, but it is +easier just to have that information by construction. +""" +function launch( + asyncDependencies::Vector{Value}, + gridSizeX::Value, + gridSizeY::Value, + gridSizeZ::Value, + blockSizeX::Value, + blockSizeY::Value, + blockSizeZ::Value, + clusterSizeX=nothing::Union{Nothing,Value}; + clusterSizeY=nothing::Union{Nothing,Value}, + clusterSizeZ=nothing::Union{Nothing,Value}, + dynamicSharedMemorySize=nothing::Union{Nothing,Value}, + asyncToken=nothing::Union{Nothing,IR.Type}, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ + asyncDependencies..., + gridSizeX, + gridSizeY, + gridSizeZ, + blockSizeX, + blockSizeY, + blockSizeZ, + ] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(clusterSizeX) && push!(_operands, clusterSizeX) + !isnothing(clusterSizeY) && push!(_operands, clusterSizeY) + !isnothing(clusterSizeZ) && push!(_operands, clusterSizeZ) + !isnothing(dynamicSharedMemorySize) && push!(_operands, dynamicSharedMemorySize) + push!( + _attributes, + operandsegmentsizes([ + length(asyncDependencies), + 1, + 1, + 1, + 1, + 1, + 1, + isnothing(clusterSizeX) ? 0 : 1, + isnothing(clusterSizeY) ? 0 : 1, + isnothing(clusterSizeZ) ? 0 : 1, + isnothing(dynamicSharedMemorySize) ? 0 : 1, + ]), + ) + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.launch", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`memcpy` + +The `gpu.memcpy` operation copies the content of one memref to another. + +The op does not execute before all async dependencies have finished +executing. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token. + +# Example + +```mlir +%token = gpu.memcpy async [%dep] %dst, %src : memref, memref +``` +""" +function memcpy( + asyncDependencies::Vector{Value}, + dst::Value, + src::Value; + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies..., dst, src] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.memcpy", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`memset` + +The `gpu.memset` operation sets the content of memref to a scalar value. + +The op does not execute before all async dependencies have finished +executing. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token. + +# Example + +```mlir +%token = gpu.memset async [%dep] %dst, %value : memref, f32 +``` +""" +function memset( + asyncDependencies::Vector{Value}, + dst::Value, + value::Value; + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies..., dst, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.memset", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`module_end` + +This op terminates the only block inside the only region of a `gpu.module`. +""" +function module_end(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "gpu.module_end", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`num_subgroups` + +Returns the number of subgroups within a workgroup. + +# Example + +```mlir +%numSg = gpu.num_subgroups : index +``` + +If `upper_bound` is set, executions with more than `upper_bound` subgroups +per workgroup cause undefined behavior. There is a default upper bound of +`kMaxDim` (currently uint32_t::max). +""" +function num_subgroups(; + result=nothing::Union{Nothing,IR.Type}, upper_bound=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.num_subgroups", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`printf` + +`gpu.printf` takes a literal format string `format` and an arbitrary number of +scalar arguments that should be printed. + +The format string is a C-style printf string, subject to any restrictions +imposed by one\'s target platform. +""" +function printf(args::Vector{Value}; format, location=Location()) + _results = IR.Type[] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("format", format),] + + return IR.create_operation( + "gpu.printf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`return_` + +A terminator operation for regions that appear in the body of `gpu.func` +functions. The operands to the `gpu.return` are the result values returned +by an invocation of the `gpu.func`. +""" +function return_(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "gpu.return", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sddmm_buffer_size` + +The `gpu.sddmm_buffer_size` operation returns the buffer size required +to perform the SDDMM operation on the given sparse and dense matrices. +The operation expects handles returned by previous sparse operations +to construct an environment and the operands for SDDMM. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +# Example + +```mlir +%buffersz, %token = gpu.sddmm_buffer_size async [%dep] %dnmatA{TRANSPOSE}, %dnmatB{TRANSPOSE}, %spmatC into f32 +``` + +The matrix arguments can also be associated with one of the following +operators: NON_TRANSPOSE, TRANSPOSE, CONJUGATE_TRANSPOSE. The default value +is NON_TRANSPOSE. +""" +function sddmm_buffer_size( + asyncDependencies::Vector{Value}, + dnmatA::Value, + dnmatB::Value, + spmatC::Value; + bufferSz::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + modeA=nothing, + modeB=nothing, + computeType, + location=Location(), +) + _results = IR.Type[bufferSz,] + _operands = Value[asyncDependencies..., dnmatA, dnmatB, spmatC] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("computeType", computeType),] + !isnothing(asyncToken) && push!(_results, asyncToken) + !isnothing(modeA) && push!(_attributes, namedattribute("modeA", modeA)) + !isnothing(modeB) && push!(_attributes, namedattribute("modeB", modeB)) + + return IR.create_operation( + "gpu.sddmm_buffer_size", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sddmm` + +The `gpu.sddmm` operation performs the SDDMM operation on the given sparse and +dense matrices, and buffer. The operation expects handles returned by previous +sparse operations to construct an environment and the operands for SDDMM. The +buffer must have been allocated on the device. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +# Example + +```mlir +%token = gpu.sddmm async [%dep] %dnmatA{TRANSPOSE}, %dnmatB{TRANSPOSE}, %spmatC, %buffer into f32 +``` + +The matrix arguments can also be associated with one of the following +operators: NON_TRANSPOSE, TRANSPOSE, CONJUGATE_TRANSPOSE. The default value +is NON_TRANSPOSE. +""" +function sddmm( + asyncDependencies::Vector{Value}, + dnmatA::Value, + dnmatB::Value, + spmatC::Value, + buffer::Value; + asyncToken=nothing::Union{Nothing,IR.Type}, + modeA=nothing, + modeB=nothing, + computeType, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies..., dnmatA, dnmatB, spmatC, buffer] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("computeType", computeType),] + !isnothing(asyncToken) && push!(_results, asyncToken) + !isnothing(modeA) && push!(_attributes, namedattribute("modeA", modeA)) + !isnothing(modeB) && push!(_attributes, namedattribute("modeB", modeB)) + + return IR.create_operation( + "gpu.sddmm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`set_csr_pointers` + +The `gpu.set_csr_pointers` assigns the given positions, coordinates, +and values buffer that reside on the device directly to the given sparse +matrix descriptor in csr format. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a `!gpu.async.token` in addition to the environment. + +# Example + +```mlir +%token = gpu.set_csr_pointers async [%dep] %positions, %coordinates, %values + : memref, memref, memref +``` +""" +function set_csr_pointers( + asyncDependencies::Vector{Value}, + spmat::Value, + positions::Value, + coordinates::Value, + values::Value; + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies..., spmat, positions, coordinates, values] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.set_csr_pointers", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`set_default_device` + +Operation that sets the current default GPU, using a zero-based index +into the set of GPUs on the system. The default GPU setting may be +thread-local. +""" +function set_default_device(devIndex::Value; location=Location()) + _results = IR.Type[] + _operands = Value[devIndex,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "gpu.set_default_device", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`shuffle` + +The \"shuffle\" op moves values to a across lanes (a.k.a., invocations, +work items) within the same subgroup. The `width` argument specifies the +number of lanes that participate in the shuffle, and must be uniform +across all lanes. Further, the first `width` lanes of the subgroup must +be active. + +The intepretation of the `offset` arguments depends on the selected +`mode`. + +Returns the `shuffleResult` and `true` if the current lane id is smaller +than `width`, and an unspecified value and `false` otherwise. + +`xor` example: + +```mlir +%1, %2 = gpu.shuffle xor %0, %offset, %width : f32 +``` + +For lane `k`, returns the value `%0` from lane `k ^ offset`. Every lane +trades value with exactly one other lane. + +`down` example: + +```mlir +%cst1 = arith.constant 1 : i32 +%3, %4 = gpu.shuffle down %0, %cst1, %width : f32 +``` + +For lane `k`, returns the value from lane `(k + 1) % width`. + +`up` example: + +```mlir +%cst1 = arith.constant 1 : i32 +%5, %6 = gpu.shuffle up %0, %cst1, %width : f32 +``` + +For lane `k`, returns the value from lane `(k - 1) % width`. + +`idx` example: + +```mlir +%cst0 = arith.constant 0 : i32 +%7, %8 = gpu.shuffle idx %0, %cst0, %width : f32 +``` + +Broadcasts the value from lane 0 to all lanes. +""" +function shuffle( + value::Value, + offset::Value, + width::Value; + shuffleResult=nothing::Union{Nothing,IR.Type}, + valid=nothing::Union{Nothing,IR.Type}, + mode, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, offset, width] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mode", mode),] + !isnothing(shuffleResult) && push!(_results, shuffleResult) + !isnothing(valid) && push!(_results, valid) + + return IR.create_operation( + "gpu.shuffle", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`spgemm_copy` + +The `gpu.spgemm_copy` operation copies the sparse matrix result of +a SpGEMM computation. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a `!gpu.async.token` in addition to the environment. + +# Example + +```mlir +gpu.spgemm_copy %spmatA, %spmatB, %spmatC, %spgemmDesc: f32 +``` + +The matrix arguments can also be associated with one of the following +operators: NON_TRANSPOSE, TRANSPOSE, CONJUGATE_TRANSPOSE. The default value +is NON_TRANSPOSE. +""" +function spgemm_copy( + asyncDependencies::Vector{Value}, + desc::Value, + spmatA::Value, + spmatB::Value, + spmatC::Value; + asyncToken=nothing::Union{Nothing,IR.Type}, + modeA=nothing, + modeB=nothing, + computeType, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies..., desc, spmatA, spmatB, spmatC] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("computeType", computeType),] + !isnothing(asyncToken) && push!(_results, asyncToken) + !isnothing(modeA) && push!(_attributes, namedattribute("modeA", modeA)) + !isnothing(modeB) && push!(_attributes, namedattribute("modeB", modeB)) + + return IR.create_operation( + "gpu.spgemm_copy", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`spgemm_create_descr` + +The `gpu.spgemm_create_descr` creates a descriptor for the SpGEMM operation. +The descriptor describes the SpGEMM operation and stores the internal data +throughout the computation. It needs to be passed as an argument to +spgemm_* operations. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a `!gpu.async.token` in addition to the environment. + +# Example + +```mlir +%desc, %token = gpu.spgemm_create_descr async [%dep] +``` +""" +function spgemm_create_descr( + asyncDependencies::Vector{Value}; + desc::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[desc,] + _operands = Value[asyncDependencies...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.spgemm_create_descr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`spgemm_destroy_descr` + +The `gpu.spgemm_destroy_descr` destroys the SpGEMM operation descriptor. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a `!gpu.async.token` in addition to the environment. + +# Example + +```mlir +%token = gpu.spgemm_destroy_descr async [%dep] %desc +``` +""" +function spgemm_destroy_descr( + asyncDependencies::Vector{Value}, + desc::Value; + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies..., desc] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.spgemm_destroy_descr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`spgemm_work_estimation_or_compute` + +The `gpu.spgemm_work_estimation_or_compute` is used to call +cusparseSpGEMM_workEstimation or cusparseSpGEMM_compute. Both of them are +for both determining the buffer size and performing the actual computation. +The operation expects handles returned by previous sparse operations to +construct an environment and the operands for SpGEMM. +The buffer must have been allocated on the device. + +C\' = alpha * op(A) * op(B) + beta * C + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a `!gpu.async.token` in addition to the environment. + +# Example + +```mlir +%bufferSz, %token = gpu.spgemm_work_estimation_or_compute async [%dep] {COMPUTE} + %desc, %spmatA{NON_TRANSPOSE}, %spmatB{NON_TRANSPOSE}, + %spmatC, %spgemmDesc, %c0, %alloc: f32 into + memref<0xi8> +``` + +The matrix arguments can also be associated with one of the following +operators: NON_TRANSPOSE, TRANSPOSE, CONJUGATE_TRANSPOSE. The default value +is NON_TRANSPOSE. +""" +function spgemm_work_estimation_or_compute( + asyncDependencies::Vector{Value}, + desc::Value, + spmatA::Value, + spmatB::Value, + spmatC::Value, + bufferSz::Value, + buffer::Value; + bufferSzNew::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + modeA=nothing, + modeB=nothing, + computeType, + kind, + location=Location(), +) + _results = IR.Type[bufferSzNew,] + _operands = Value[asyncDependencies..., desc, spmatA, spmatB, spmatC, bufferSz, buffer] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("computeType", computeType), namedattribute("kind", kind) + ] + !isnothing(asyncToken) && push!(_results, asyncToken) + !isnothing(modeA) && push!(_attributes, namedattribute("modeA", modeA)) + !isnothing(modeB) && push!(_attributes, namedattribute("modeB", modeB)) + + return IR.create_operation( + "gpu.spgemm_work_estimation_or_compute", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`spmm_buffer_size` + +The `gpu.spmm_buffer_size` operation returns the buffer size required +to perform the SpMM operation on the given sparse and dense matrix. +The operation expects handles returned by previous sparse operations +to construct an environment and the operands for SpMM. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +The matrix arguments can also be associated with one of the following +operators: NON_TRANSPOSE, TRANSPOSE, CONJUGATE_TRANSPOSE. The default value +is NON_TRANSPOSE. + +# Example + +```mlir +%bufferszs, %token = gpu.spmm_buffer_size async [%dep] %spmatA{TRANSPOSE}, %dnmatB{TRANSPOSE}, %dnmatC : i64 into f32 +``` +""" +function spmm_buffer_size( + asyncDependencies::Vector{Value}, + spmatA::Value, + dnmatB::Value, + dnmatC::Value; + bufferSzs::Vector{IR.Type}, + asyncToken=nothing::Union{Nothing,IR.Type}, + modeA=nothing, + modeB=nothing, + computeType, + location=Location(), +) + _results = IR.Type[bufferSzs...,] + _operands = Value[asyncDependencies..., spmatA, dnmatB, dnmatC] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("computeType", computeType),] + !isnothing(asyncToken) && push!(_results, asyncToken) + !isnothing(modeA) && push!(_attributes, namedattribute("modeA", modeA)) + !isnothing(modeB) && push!(_attributes, namedattribute("modeB", modeB)) + + return IR.create_operation( + "gpu.spmm_buffer_size", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`spmm` + +The `gpu.spmm` operation performs the SpMM operation on the given sparse and +dense matrix, and buffer. The operation expects handles returned by previous +sparse operations to construct an environment and the operands for SpMM. The +buffer must have been allocated on the device. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +The matrix arguments can also be associated with one of the following +operators: NON_TRANSPOSE, TRANSPOSE, CONJUGATE_TRANSPOSE. The default value +is NON_TRANSPOSE. + +# Example + +```mlir +%token = gpu.spmm async [%dep] %spmatA{TRANSPOSE}, %dnmatB{TRANSPOSE}, %dnmatC, %buffers : type(\$buffers) into f32 +``` +""" +function spmm( + asyncDependencies::Vector{Value}, + spmatA::Value, + dnmatB::Value, + dnmatC::Value, + buffers::Vector{Value}; + asyncToken=nothing::Union{Nothing,IR.Type}, + modeA=nothing, + modeB=nothing, + computeType, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies..., spmatA, dnmatB, dnmatC, buffers...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("computeType", computeType),] + push!( + _attributes, + operandsegmentsizes([length(asyncDependencies), 1, 1, 1, length(buffers)]), + ) + !isnothing(asyncToken) && push!(_results, asyncToken) + !isnothing(modeA) && push!(_attributes, namedattribute("modeA", modeA)) + !isnothing(modeB) && push!(_attributes, namedattribute("modeB", modeB)) + + return IR.create_operation( + "gpu.spmm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`spmv_buffer_size` + +The `gpu.spmv_buffer_size` operation returns the buffer size required +to perform the SpMV operation on the given sparse matrix and dense vectors. +The operation expects handles returned by previous sparse operations +to construct an environment and the operands for SpMV. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +The matrix arguments can also be associated with one of the following +operators: NON_TRANSPOSE, TRANSPOSE, CONJUGATE_TRANSPOSE. The default value +is NON_TRANSPOSE. + +# Example + +```mlir +%buffersz, %token = gpu.spmv_buffer_size async [%dep] %spmatA{TRANSPOSE}, %dnX, %dnY into f32 +``` +""" +function spmv_buffer_size( + asyncDependencies::Vector{Value}, + spmatA::Value, + dnX::Value, + dnY::Value; + bufferSz::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + modeA=nothing, + computeType, + location=Location(), +) + _results = IR.Type[bufferSz,] + _operands = Value[asyncDependencies..., spmatA, dnX, dnY] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("computeType", computeType),] + !isnothing(asyncToken) && push!(_results, asyncToken) + !isnothing(modeA) && push!(_attributes, namedattribute("modeA", modeA)) + + return IR.create_operation( + "gpu.spmv_buffer_size", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`spmv` + +The `gpu.spmv` operation performs the SpMV operation on the given sparse matrix, +dense vectors, and buffer. The operation expects handles returned by previous +sparse operations to construct an environment and the operands for SpMV. The +buffer must have been allocated on the device. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a !gpu.async.token in addition to the environment. + +The matrix arguments can also be associated with one of the following +operators: NON_TRANSPOSE, TRANSPOSE, CONJUGATE_TRANSPOSE. The default value +is NON_TRANSPOSE. + +# Example + +```mlir +%token = gpu.spmv async [%dep] %spmatA{TRANSPOSE}, %dnX, %dnY : memref into bf16 +``` +""" +function spmv( + asyncDependencies::Vector{Value}, + spmatA::Value, + dnX::Value, + dnY::Value, + buffer::Value; + asyncToken=nothing::Union{Nothing,IR.Type}, + modeA=nothing, + computeType, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies..., spmatA, dnX, dnY, buffer] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("computeType", computeType),] + !isnothing(asyncToken) && push!(_results, asyncToken) + !isnothing(modeA) && push!(_attributes, namedattribute("modeA", modeA)) + + return IR.create_operation( + "gpu.spmv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`spmat_get_size` + +The `gpu.spmat_get_size` operation retrieves the number of rows, number of +columns, and number of non-zero elements of a sparse matrix. + +If the `async` keyword is present, the op is executed asynchronously (i.e. +it does not block until the execution has finished on the device). In +that case, it returns a `!gpu.async.token` in addition to the environment. + +# Example + +```mlir +%rows, %cols, %nnz, %token = gpu.spmat_get_size async [%dep] %spmatC +``` +""" +function spmat_get_size( + asyncDependencies::Vector{Value}, + spmat::Value; + rows::IR.Type, + cols::IR.Type, + nnz::IR.Type, + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[rows, cols, nnz] + _operands = Value[asyncDependencies..., spmat] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.spmat_get_size", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`subgroup_id` + +Returns the subgroup id, i.e., the index of the current subgroup within the +workgroup. + +# Example + +```mlir +%sgId = gpu.subgroup_id : index +``` + +Executions where there are more than `upper_bound` subgroups per workgroup +cause undefined behavior. There is an implicit upper bound of `kMaxDim` +(currently uint32_t::max). +""" +function subgroup_id(; + result=nothing::Union{Nothing,IR.Type}, upper_bound=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.subgroup_id", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`subgroup_mma_compute` + +The `gpu.subgroup_mma_compute` operation performs a matrix-multiply accumulate (mma) +operation using all the threads in a subgroup. + +This operation takes three `!gpu.mma_matrix`s as arguments: these hold `A`, +`B` and `C`operands for the mma operation. The operation performed is represented +as `C += A * B`. The op returns a `!gpu.mma_matrix` which contains the result of +the operation held by all threads in a subgroup. `a_transpose` or +`b_transpose` if present, signify that the respective operand was loaded in a +transposed manner. The transpose operands are required to map to correct +underlying intrisics but they currently do not seem to affect correctness +even if they are absent given that the operands were loaded correctly using +the `transpose` attribute in `gpu.subgroup_mma_load_matrix` op. + +For integer types, the `A` and `B` matrices carry their signedness with their +types. The accumulator type is expected to be signless and imply a signed integer +with a greater width than the other two operands. + +This op is meant to be used along with `gpu.subgroup_mma_store_matrix` and +`gpu.subgroup_mma_load_matrix` ops. + +# Example + +```mlir +%D = gpu.subgroup_mma_compute_matrix %A, %B, %C : + !gpu.mma_matrix<16x16xf16, \"AOp\">, !gpu.mma_matrix<16x16xf16, \"BOp\">> + -> !gpu.mma_matrix<16x16xf16, \"COp\"> +``` +""" +function subgroup_mma_compute( + opA::Value, + opB::Value, + opC::Value; + res=nothing::Union{Nothing,IR.Type}, + a_transpose=nothing, + b_transpose=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[opA, opB, opC] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(a_transpose) && + push!(_attributes, namedattribute("a_transpose", a_transpose)) + !isnothing(b_transpose) && + push!(_attributes, namedattribute("b_transpose", b_transpose)) + + return IR.create_operation( + "gpu.subgroup_mma_compute", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`subgroup_mma_constant_matrix` + +The `gpu.subgroup_mma_constant_matrix` creates a `!gpu.mma_matrix` with +constant elements. + +The operation takes a scalar input and return a `!gpu.mma_matrix` where +each element of is equal to the operand constant. The destination +mma_matrix type must have elememt type equal to the constant type. Since +the layout of `!gpu.mma_matrix` is opaque this only support setting all the +elements to the same value. + +This op is meant to be used along with `gpu.subgroup_mma_compute`. + +# Example + +```mlir + %0 = gpu.subgroup_mma_constant_matrix %a : + !gpu.mma_matrix<16x16xf16, \"AOp\"> + %1 = gpu.subgroup_mma_constant_matrix %b : + !gpu.mma_matrix<16x16xf32, \"COp\"> +``` +""" +function subgroup_mma_constant_matrix(value::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "gpu.subgroup_mma_constant_matrix", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`subgroup_mma_elementwise` + +The `gpu.subgroup_mma_elementwise` takes `!gpu.mma_matrix` inputs and +compute a new `!gpu.mma_matrix` by applying an elementwise operation to each +element. + +Since the operation is elementwise and the matrix type must match, the +matrix elements are processed independently of the matrix layout. + +This op is meant to be used along with `gpu.subgroup_mma_compute`. + +# Example + +```mlir + %0 = %A, %B { opType = \"ADD\" } : + (!gpu.mma_matrix<16x16xf16, \"COp\">, !gpu.mma_matrix<16x16xf16, \"COp\">) + -> !gpu.mma_matrix<16x16xf16, \"COp\"> +``` +""" +function subgroup_mma_elementwise( + args::Vector{Value}; res::IR.Type, opType, location=Location() +) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("opType", opType),] + + return IR.create_operation( + "gpu.subgroup_mma_elementwise", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`subgroup_mma_load_matrix` + +The `gpu.subgroup_mma_load_matrix` operation loads a matrix collectively +using all the threads in a subgroup. + +This operation takes a memref as its first operand: it is the source matrix +from which data is to be loaded. The op returns a `!gpu.mma_matrix`. The +source memref can be in global memory or shared memory. The load address is +determined using `indices`. The matrix being loaded into is the result. The +`leadDimension` attribute specifies the leading dimension size of the source +matrix which eventually allows the lowering to determine the size of each +row. If the `transpose` attribute is present then the op does a transposed load. + +For integer types, the resulting `!gpu.mma_matrix` type needs to specify the +signedness of the data if the matrix type is an `A` or `B` operand for +`gpu.subgroup_mma_compute`. + +This op is often meant to be used along with `gpu.subgroup_mma_store_matrix` and +`gpu.subgroup_mma_compute`. + +# Example + +```mlir + %0 = gpu.subgroup_mma_load_matrix src[%i,%j] : {leadDimension = 32 : i32} + : memref<32x32xf16, 3>, !gpu.mma_matrix<16x16xf16, \"AOp\"> +``` +""" +function subgroup_mma_load_matrix( + srcMemref::Value, + indices::Vector{Value}; + res::IR.Type, + leadDimension, + transpose=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[srcMemref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("leadDimension", leadDimension),] + !isnothing(transpose) && push!(_attributes, namedattribute("transpose", transpose)) + + return IR.create_operation( + "gpu.subgroup_mma_load_matrix", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`subgroup_mma_store_matrix` + +The `gpu.subgroup_mma_store_matrix` operation stores a matrix collectively +using all the threads in a subgroup. + +This operation takes a `!gpu.mma_matrix` and a memref as operands. +`!gpu.mma_matrix` is the source value containing the data to be stored into the +destination memref which can be in global or shared memory. The store address +is determined using the indices provided. The `leadDimension` attribute +specifies the leading dimension of the destination matrix. If the +`transpose` attribute is present then the op does a transposed store. + +This op is often meant to be used along with `gpu.subgroup_mma_load_matrix` and +`gpu.subgroup_mma_compute`. + +# Example + +```mlir +gpu.subgroup_mma_store_matrix %D, %sg[%i,%j] : { leadDimension = 32 : i32} + : !gpu.mma_matrix<16x16xf16, \"COp\">, memref<32x32xf16, 3> +``` +""" +function subgroup_mma_store_matrix( + src::Value, + dstMemref::Value, + indices::Vector{Value}; + leadDimension, + transpose=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[src, dstMemref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("leadDimension", leadDimension),] + !isnothing(transpose) && push!(_attributes, namedattribute("transpose", transpose)) + + return IR.create_operation( + "gpu.subgroup_mma_store_matrix", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`subgroup_reduce` + +The `subgroup_reduce` op reduces the value of every lane (work item) across +a subgroup. The result is equal for all lanes. + +When the reduced value is of a vector type, each vector element is reduced +independently. Only 1-d vector types are allowed. + +# Example + +```mlir +%1 = gpu.subgroup_reduce add %a : (f32) -> (f32) +%2 = gpu.subgroup_reduce add %b : (vector<4xf16>) -> (vector<4xf16>) +``` + +If `uniform` flag is set either none or all lanes of a subgroup need to execute +this op in convergence. The reduction operation must be one +of: +* Integer types: `add`, `mul`, `minui`, `minsi`, `maxui`, `maxsi`, `and`, + `or`, `xor` +* Floating point types: `add`, `mul`, `minnumf`, `maxnumf`, `minimumf`, + `maximumf` +""" +function subgroup_reduce( + value::Value; + result=nothing::Union{Nothing,IR.Type}, + op, + uniform=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("op", op),] + !isnothing(result) && push!(_results, result) + !isnothing(uniform) && push!(_attributes, namedattribute("uniform", uniform)) + + return IR.create_operation( + "gpu.subgroup_reduce", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`subgroup_size` + +Returns the number of threads within a subgroup. + +# Example + +```mlir +%sgSz = gpu.subgroup_size : index +``` + +Executions where the number of threads per subgroup exceed `upper_bound` cause +undefined behavior. When no `upper_bound` is specified, range analyses and +similar machinery assume the default bound of `kMaxSubgroupSize`, currently +128. +""" +function subgroup_size(; + result=nothing::Union{Nothing,IR.Type}, upper_bound=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.subgroup_size", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`terminator` + +A terminator operation for regions that appear in the body of `gpu.launch` +operation. These regions are not expected to return any value so the +terminator takes no operands. +""" +function terminator(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "gpu.terminator", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`thread_id` + +Returns the thread id, i.e. the index of the current thread within the block +along the x, y, or z `dimension`. + +# Example + +```mlir +%tIdX = gpu.thread_id x +``` + +If `upper_bound` is set, or if one can be inferred from `known_block_size`-type +annotations in context, executions where the thread index would be greater +than or equal to that bound cause undefined behavior. + +There is an implicit upper bound of `kMaxDim` (currently uint32_t::max). +""" +function thread_id(; + result_0=nothing::Union{Nothing,IR.Type}, + dimension, + upper_bound=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimension", dimension),] + !isnothing(result_0) && push!(_results, result_0) + !isnothing(upper_bound) && + push!(_attributes, namedattribute("upper_bound", upper_bound)) + + return IR.create_operation( + "gpu.thread_id", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`wait` + +This op synchronizes the host or the device with a list of dependent ops. + +If the op contains the `async` keyword, it returns a new async token which +is synchronized with the op arguments. This new token is merely a shortcut +to the argument list, and one could replace the uses of the result with the +arguments for the same effect. The async version of this op is primarily +used to make each async token have a single use during lowering and +thereby make forks in async execution explicit. Example usage: + +```mlir +%t0 = gpu.foo async : !gpu.async.token +%t1 = gpu.bar async : !gpu.async.token +%t2 = gpu.wait async [%t0, %t1] +// gpu.baz doesn\'t run until gpu.foo and gpu.bar have both completed, just +// as if the async dependencies were [%t0, %t1]. +%t3 = gpu.baz async [%t2] +``` + +If the op does not contain the `async` keyword, it does not return a new +async token but blocks until all ops producing the async dependency tokens +finished execution. All dependent memory operations are visible to the host +once this op completes. Example usage: + +```mlir +%t0 = gpu.foo async : !gpu.async.token +%t1 = gpu.bar async : !gpu.async.token +// The gpu.wait op blocks until gpu.foo and gpu.bar have completed. +gpu.wait [%t0, %t1] +``` +""" +function wait( + asyncDependencies::Vector{Value}; + asyncToken=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncDependencies...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncToken) && push!(_results, asyncToken) + + return IR.create_operation( + "gpu.wait", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +gpu.yield` is a special terminator operation for blocks inside regions +in gpu ops. It returns values to the immediately enclosing gpu op. + +# Example + +```mlir +gpu.yield %f0, %f1 : f32, f32 +``` +""" +function yield(values::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[values...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "gpu.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # gpu diff --git a/src/Dialects/19/IRDL.jl b/src/Dialects/19/IRDL.jl new file mode 100644 index 00000000..809b0fd3 --- /dev/null +++ b/src/Dialects/19/IRDL.jl @@ -0,0 +1,832 @@ +module irdl + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`all_of` + +`irdl.all_of` defines a constraint that accepts any type or attribute that +satisfies all of its provided constraints. + +# Example + +```mlir +irdl.dialect cmath { + irdl.type complex_f32 { + %0 = irdl.is i32 + %1 = irdl.is f32 + %2 = irdl.any_of(%0, %1) // is 32-bit + + %3 = irdl.is f32 + %4 = irdl.is f64 + %5 = irdl.any_of(%3, %4) // is a float + + %6 = irdl.all_of(%2, %5) // is a 32-bit float + irdl.parameters(%6) + } +} +``` + +The above program defines a type `complex` inside the dialect `cmath` that +can has one parameter that must be 32-bit long and a float (in other +words, that must be `f32`). +""" +function all_of( + args::Vector{Value}; output=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "irdl.all_of", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`any_of` + +`irdl.any_of` defines a constraint that accepts any type or attribute that +satisfies at least one of its provided type constraints. + +# Example + +```mlir +irdl.dialect cmath { + irdl.type complex { + %0 = irdl.is i32 + %1 = irdl.is i64 + %2 = irdl.is f32 + %3 = irdl.is f64 + %4 = irdl.any_of(%0, %1, %2, %3) + irdl.parameters(%4) + } +} +``` + +The above program defines a type `complex` inside the dialect `cmath` that +can have a single type parameter that can be either `i32`, `i64`, `f32` or +`f32`. +""" +function any_of( + args::Vector{Value}; output=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "irdl.any_of", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`any` + +`irdl.any` defines a constraint that accepts any type or attribute. + +# Example + +```mlir +irdl.dialect @cmath { + irdl.type @complex_flexible { + %0 = irdl.any + irdl.parameters(%0) + } +} +``` + +The above program defines a type `complex_flexible` inside the dialect +`cmath` that has a single parameter that can be any attribute. +""" +function any(; output=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "irdl.any", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`attribute` + +`irdl.attribute` defines a new attribute belonging to the `irdl.dialect` +parent. + +The attribute parameters can be defined with an `irdl.parameters` operation +in the optional region. + +# Example + +```mlir +irdl.dialect @testd { + irdl.attribute @enum_attr { + %0 = irdl.is \"foo\" + %1 = irdl.is \"bar\" + %2 = irdl.any_of(%0, %1) + irdl.parameters(%2) + } +} +``` + +The above program defines an `enum_attr` attribute inside the `testd` +dialect. The attribute has one `StringAttr` parameter that should be +either a `\"foo\"` or a `\"bar\"`. +""" +function attribute(; sym_name, body::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("sym_name", sym_name),] + + return IR.create_operation( + "irdl.attribute", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`attributes` + +`irdl.attributes` defines the attributes of the `irdl.operation` parent +operation definition. + +In the following example, `irdl.attributes` defines the attributes of the +`attr_op` operation: + +```mlir +irdl.dialect @example { + + irdl.operation @attr_op { + %0 = irdl.any + %1 = irdl.is i64 + irdl.attibutes { + \"attr1\" = %0, + \"attr2\" = %1 + } + } +} +``` + +The operation will expect an arbitrary attribute \"attr1\" and an +attribute \"attr2\" with value `i64`. +""" +function attributes( + attributeValues::Vector{Value}; attributeValueNames, location=Location() +) + _results = IR.Type[] + _operands = Value[attributeValues...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("attributeValueNames", attributeValueNames),] + + return IR.create_operation( + "irdl.attributes", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`base` + +`irdl.base` defines a constraint that only accepts a single type +or attribute base, e.g. an `IntegerType`. The attribute base is defined +either by a symbolic reference to the corresponding IRDL definition, +or by the name of the base. Named bases are prefixed with `!` or `#` +respectively for types and attributes. + +# Example + +```mlir +irdl.dialect @cmath { + irdl.type @complex { + %0 = irdl.base \"!builtin.integer\" + irdl.parameters(%0) + } + + irdl.type @complex_wrapper { + %0 = irdl.base @complex + irdl.parameters(%0) + } +} +``` + +The above program defines a `cmath.complex` type that expects a single +parameter, which is a type with base name `builtin.integer`, which is the +name of an `IntegerType` type. +It also defines a `cmath.complex_wrapper` type that expects a single +parameter, which is a type of base type `cmath.complex`. +""" +function base(; + output=nothing::Union{Nothing,IR.Type}, + base_ref=nothing, + base_name=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(output) && push!(_results, output) + !isnothing(base_ref) && push!(_attributes, namedattribute("base_ref", base_ref)) + !isnothing(base_name) && push!(_attributes, namedattribute("base_name", base_name)) + + return IR.create_operation( + "irdl.base", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`c_pred` + +`irdl.c_pred` defines a constraint that is written in C++. + +Dialects using this operation cannot be registered at runtime, as it relies +on C++ code. + +Special placeholders can be used to refer to entities in the context where +this predicate is used. They serve as \"hooks\" to the enclosing environment. +The following special placeholders are supported in constraints for an op: + +* `\$_builder` will be replaced by a mlir::Builder instance. +* `\$_op` will be replaced by the current operation. +* `\$_self` will be replaced with the entity this predicate is attached to. + Compared to ODS, `\$_self` is always of type `mlir::Attribute`, and types + are manipulated as `TypeAttr` attributes. + +# Example +```mlir +irdl.type @op_with_attr { + %0 = irdl.c_pred \"::llvm::isa<::mlir::IntegerAttr>(\$_self)\" + irdl.parameters(%0) +} +``` + +In this example, @op_with_attr is defined as a type with a single +parameter, which is an `IntegerAttr`, as constrained by the C++ predicate. +""" +function c_pred(; output=nothing::Union{Nothing,IR.Type}, pred, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("pred", pred),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "irdl.c_pred", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`dialect` + +The `irdl.dialect` operation defines a dialect. All operations, attributes, +and types defined inside its region will be part of the dialect. + +# Example + +```mlir +irdl.dialect @cmath { + ... +} +``` + +The above program defines a `cmath` dialect. +""" +function dialect(; sym_name, body::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("sym_name", sym_name),] + + return IR.create_operation( + "irdl.dialect", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`is` + +`irdl.is` defines a constraint that only accepts a specific instance of a +type or attribute. + +# Example + +```mlir +irdl.dialect @cmath { + irdl.type @complex_i32 { + %0 = irdl.is i32 + irdl.parameters(%0) + } +} +``` + +The above program defines a `complex_i32` type inside the dialect `cmath` +that can only have a `i32` as its parameter. +""" +function is(; output=nothing::Union{Nothing,IR.Type}, expected, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("expected", expected),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "irdl.is", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`operands` + +`irdl.operands` define the operands of the `irdl.operation` parent operation +definition. + +In the following example, `irdl.operands` defines the operands of the +`norm` operation: + +```mlir +irdl.dialect @cmath { + + irdl.type @complex { /* ... */ } + + irdl.operation @mul { + %0 = irdl.any + %1 = irdl.parametric @complex<%0> + irdl.results(%1) + irdl.operands(%1, %1) + } +} +``` + +The `mul` operation will expect two operands of type `cmath.complex`, that +have the same type, and return a result of the same type. + +The operands can also be marked as variadic or optional: +```mlir +irdl.operands(%0, single %1, optional %2, variadic %3) +``` + +Here, %0 and %1 are required single operands, %2 is an optional operand, +and %3 is a variadic operand. + +When more than one operand is marked as optional or variadic, the operation +will expect a \'operandSegmentSizes\' attribute that defines the number of +operands in each segment. +""" +function operands(args::Vector{Value}; variadicity, location=Location()) + _results = IR.Type[] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("variadicity", variadicity),] + + return IR.create_operation( + "irdl.operands", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`operation` + +`irdl.operation` defines a new operation belonging to the `irdl.dialect` +parent. + +Operations can define constraints on their operands and results with the +`irdl.results` and `irdl.operands` operations. If these operations are not +present in the region, the results or operands are expected to be empty. + +# Example + +```mlir +irdl.dialect @cmath { + + irdl.type @complex { /* ... */ } + + irdl.operation @norm { + %0 = irdl.any + %1 = irdl.parametric @complex<%0> + irdl.results(%0) + irdl.operands(%1) + } +} +``` + +The above program defines an operation `norm` inside the dialect `cmath`. +The operation expects a single operand of base type `cmath.complex`, and +returns a single result of the element type of the operand. +""" +function operation(; sym_name, body::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("sym_name", sym_name),] + + return IR.create_operation( + "irdl.operation", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`parameters` + +`irdl.parameters` defines the constraints on parameters of a type or +attribute definition. + +# Example + +```mlir +irdl.dialect @cmath { + irdl.type @complex { + %0 = irdl.is i32 + %1 = irdl.is i64 + %2 = irdl.any_of(%0, %1) + irdl.parameters(%2) + } +} +``` + +The above program defines a type `complex` inside the dialect `cmath`. The +type has a single parameter that should be either `i32` or `i64`. +""" +function parameters(args::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "irdl.parameters", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`parametric` + +`irdl.parametric` defines a constraint that accepts only a single type +or attribute base. The attribute base is defined by a symbolic reference +to the corresponding definition. It will additionally constraint the +parameters of the type/attribute. + +# Example + +```mlir +irdl.dialect @cmath { + + irdl.type @complex { /* ... */ } + + irdl.operation @norm { + %0 = irdl.any + %1 = irdl.parametric @complex<%0> + irdl.operands(%1) + irdl.results(%0) + } +} +``` + +The above program defines an operation `norm` inside the dialect `cmath` that +for any `T` takes a `cmath.complex` with parameter `T` and returns a `T`. +""" +function parametric( + args::Vector{Value}; + output=nothing::Union{Nothing,IR.Type}, + base_type, + location=Location(), +) + _results = IR.Type[] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("base_type", base_type),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "irdl.parametric", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`region` + +The irdl.region construct defines a set of characteristics +that a region of an operation should satify. + +These characteristics include constraints for the entry block arguments +of the region and the total number of blocks it contains. +The number of blocks must be a non-zero and non-negative integer, +and it is optional by default. +The set of constraints for the entry block arguments may be optional or +empty. If no parentheses are provided, the set is assumed to be optional, +and the arguments are not constrained in any way. If parentheses are +provided with no arguments, it means that the region must have +no entry block arguments + + +# Example + +```mlir +irdl.dialect @example { + irdl.operation @op_with_regions { + %r0 = irdl.region + %r1 = irdl.region() + %v0 = irdl.is i32 + %v1 = irdl.is i64 + %r2 = irdl.region(%v0, %v1) + %r3 = irdl.region with size 3 + + irdl.regions(%r0, %r1, %r2, %r3) + } +} +``` + +The above snippet demonstrates an operation named `@op_with_regions`, +which is constrained to have four regions. + +* Region `%r0` doesn\'t have any constraints on the arguments + or the number of blocks. +* Region `%r1` should have an empty set of arguments. +* Region `%r2` should have two arguments of types `i32` and `i64`. +* Region `%r3` should contain exactly three blocks. +""" +function region( + entryBlockArgs::Vector{Value}; + output=nothing::Union{Nothing,IR.Type}, + numberOfBlocks=nothing, + constrainedArguments=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[entryBlockArgs...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(output) && push!(_results, output) + !isnothing(numberOfBlocks) && + push!(_attributes, namedattribute("numberOfBlocks", numberOfBlocks)) + !isnothing(constrainedArguments) && + push!(_attributes, namedattribute("constrainedArguments", constrainedArguments)) + + return IR.create_operation( + "irdl.region", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`regions` + +`irdl.regions` defines the regions of an operation by accepting +values produced by `irdl.region` operation as arguments. + +# Example + +```mlir +irdl.dialect @example { + irdl.operation @op_with_regions { + %r1 = irdl.region with size 3 + %0 = irdl.any + %r2 = irdl.region(%0) + irdl.regions(%r1, %r2) + } +} +``` + +In the snippet above the operation is constrained to have two regions. +The first region should contain three blocks. +The second region should have one region with one argument. +""" +function regions(args::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "irdl.regions", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`results` + +`irdl.results` define the results of the `irdl.operation` parent operation +definition. + +In the following example, `irdl.results` defines the results of the +`norm` operation: + +```mlir +irdl.dialect @cmath { + + irdl.type @complex { /* ... */ } + + irdl.operation @get_values { + %0 = irdl.any + %1 = irdl.parametric @complex<%0> + irdl.results(%0, %0) + irdl.operands(%1) + } +} +``` + +The operation will expect one operand of the `cmath.complex` type, and two +results that have the underlying type of the `cmath.complex`. + +The results can also be marked as variadic or optional: +```mlir +irdl.results(%0, single %1, optional %2, variadic %3) +``` + +Here, %0 and %1 are required single results, %2 is an optional result, +and %3 is a variadic result. + +When more than one result is marked as optional or variadic, the operation +will expect a \'resultSegmentSizes\' attribute that defines the number of +results in each segment. +""" +function results(args::Vector{Value}; variadicity, location=Location()) + _results = IR.Type[] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("variadicity", variadicity),] + + return IR.create_operation( + "irdl.results", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`type` + +`irdl.type` defines a new type belonging to the `irdl.dialect` parent. + +The type parameters can be defined with an `irdl.parameters` operation in +the optional region. + +# Example + +```mlir +irdl.dialect @cmath { + irdl.type @complex { + %0 = irdl.is i32 + %1 = irdl.is i64 + %2 = irdl.any_of(%0, %1) + irdl.parameters(%2) + } +} +``` + +The above program defines a type `complex` inside the dialect `cmath`. The +type has a single parameter that should be either `i32` or `i64`. +""" +function type(; sym_name, body::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("sym_name", sym_name),] + + return IR.create_operation( + "irdl.type", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # irdl diff --git a/src/Dialects/19/Index.jl b/src/Dialects/19/Index.jl new file mode 100644 index 00000000..98a03e8f --- /dev/null +++ b/src/Dialects/19/Index.jl @@ -0,0 +1,962 @@ +module index + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`add` + +The `index.add` operation takes two index values and computes their sum. + +# Example + +```mlir +// c = a + b +%c = index.add %a, %b +``` +""" +function add( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`and` + +The `index.and` operation takes two index values and computes their bitwise +and. + +# Example + +```mlir +// c = a & b +%c = index.and %a, %b +``` +""" +function and( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.and", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`bool_constant` + +The `index.bool.constant` operation produces an bool-typed SSA value equal +to either `true` or `false`. + +This operation is used to materialize bool constants that arise when folding +`index.cmp`. + +# Example + +```mlir +%0 = index.bool.constant true +``` +""" +function bool_constant(; result=nothing::Union{Nothing,IR.Type}, value, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.bool.constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`casts` + +The `index.casts` operation enables conversions between values of index type +and concrete fixed-width integer types. If casting to a wider integer, the +value is sign-extended. If casting to a narrower integer, the value is +truncated. + +# Example + +```mlir +// Cast to i32 +%0 = index.casts %a : index to i32 + +// Cast from i64 +%1 = index.casts %b : i64 to index +``` +""" +function casts(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "index.casts", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`castu` + +The `index.castu` operation enables conversions between values of index type +and concrete fixed-width integer types. If casting to a wider integer, the +value is zero-extended. If casting to a narrower integer, the value is +truncated. + +# Example + +```mlir +// Cast to i32 +%0 = index.castu %a : index to i32 + +// Cast from i64 +%1 = index.castu %b : i64 to index +``` +""" +function castu(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "index.castu", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ceildivs` + +The `index.ceildivs` operation takes two index values and computes their +signed quotient. Treats the leading bit as the sign and rounds towards +positive infinity, i.e. `7 / -2 = -3`. + +Note: division by zero and signed division overflow are undefined behaviour. + +# Example + +```mlir +// c = ceil(a / b) +%c = index.ceildivs %a, %b +``` +""" +function ceildivs( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.ceildivs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ceildivu` + +The `index.ceildivu` operation takes two index values and computes their +unsigned quotient. Treats the leading bit as the most significant and rounds +towards positive infinity, i.e. `6 / -2 = 1`. + +Note: division by zero is undefined behaviour. + +# Example + +```mlir +// c = ceil(a / b) +%c = index.ceildivu %a, %b +``` +""" +function ceildivu( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.ceildivu", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cmp` + +The `index.cmp` operation takes two index values and compares them according +to the comparison predicate and returns an `i1`. The following comparisons +are supported: + +- `eq`: equal +- `ne`: not equal +- `slt`: signed less than +- `sle`: signed less than or equal +- `sgt`: signed greater than +- `sge`: signed greater than or equal +- `ult`: unsigned less than +- `ule`: unsigned less than or equal +- `ugt`: unsigned greater than +- `uge`: unsigned greater than or equal + +The result is `1` if the comparison is true and `0` otherwise. + +# Example + +```mlir +// Signed less than comparison. +%0 = index.cmp slt(%a, %b) + +// Unsigned greater than or equal comparison. +%1 = index.cmp uge(%a, %b) + +// Not equal comparison. +%2 = index.cmp ne(%a, %b) +``` +""" +function cmp( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + pred, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("pred", pred),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.cmp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`constant` + +The `index.constant` operation produces an index-typed SSA value equal to +some index-typed integer constant. + +# Example + +```mlir +%0 = index.constant 42 +``` +""" +function constant(; result=nothing::Union{Nothing,IR.Type}, value, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`divs` + +The `index.divs` operation takes two index values and computes their signed +quotient. Treats the leading bit as the sign and rounds towards zero, i.e. +`6 / -2 = -3`. + +Note: division by zero and signed division overflow are undefined behaviour. + +# Example + +```mlir +// c = a / b +%c = index.divs %a, %b +``` +""" +function divs( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.divs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`divu` + +The `index.divu` operation takes two index values and computes their +unsigned quotient. Treats the leading bit as the most significant and rounds +towards zero, i.e. `6 / -2 = 0`. + +Note: division by zero is undefined behaviour. + +# Example + +```mlir +// c = a / b +%c = index.divu %a, %b +``` +""" +function divu( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.divu", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`floordivs` + +The `index.floordivs` operation takes two index values and computes their +signed quotient. Treats the leading bit as the sign and rounds towards +negative infinity, i.e. `5 / -2 = -3`. + +Note: division by zero and signed division overflow are undefined behaviour. + +# Example + +```mlir +// c = floor(a / b) +%c = index.floordivs %a, %b +``` +""" +function floordivs( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.floordivs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`maxs` + +The `index.maxs` operation takes two index values and computes their signed +maximum value. Treats the leading bit as the sign, i.e. `max(-2, 6) = 6`. + +# Example + +```mlir +// c = max(a, b) +%c = index.maxs %a, %b +``` +""" +function maxs( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.maxs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`maxu` + +The `index.maxu` operation takes two index values and computes their +unsigned maximum value. Treats the leading bit as the most significant, i.e. +`max(15, 6) = 15` or `max(-2, 6) = -2`. + +# Example + +```mlir +// c = max(a, b) +%c = index.maxu %a, %b +``` +""" +function maxu( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.maxu", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mins` + +The `index.mins` operation takes two index values and computes their signed +minimum value. Treats the leading bit as the sign, i.e. `min(-2, 6) = -2`. + +# Example + +```mlir +// c = min(a, b) +%c = index.mins %a, %b +``` +""" +function mins( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.mins", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`minu` + +The `index.minu` operation takes two index values and computes their +unsigned minimum value. Treats the leading bit as the most significant, i.e. +`min(15, 6) = 6` or `min(-2, 6) = 6`. + +# Example + +```mlir +// c = min(a, b) +%c = index.minu %a, %b +``` +""" +function minu( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.minu", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mul` + +The `index.mul` operation takes two index values and computes their product. + +# Example + +```mlir +// c = a * b +%c = index.mul %a, %b +``` +""" +function mul( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`or` + +The `index.or` operation takes two index values and computes their bitwise +or. + +# Example + +```mlir +// c = a | b +%c = index.or %a, %b +``` +""" +function or( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.or", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`rems` + +The `index.rems` operation takes two index values and computes their signed +remainder. Treats the leading bit as the sign, i.e. `6 % -2 = 0`. + +# Example + +```mlir +// c = a % b +%c = index.rems %a, %b +``` +""" +function rems( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.rems", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`remu` + +The `index.remu` operation takes two index values and computes their +unsigned remainder. Treats the leading bit as the most significant, i.e. +`6 % -2 = 6`. + +# Example + +```mlir +// c = a % b +%c = index.remu %a, %b +``` +""" +function remu( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.remu", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`shl` + +The `index.shl` operation shifts an index value to the left by a variable +amount. The low order bits are filled with zeroes. The RHS operand is always +treated as unsigned. If the RHS operand is equal to or greater than the +index bitwidth, the result is a poison value. + +# Example + +```mlir +// c = a << b +%c = index.shl %a, %b +``` +""" +function shl( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.shl", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`shrs` + +The `index.shrs` operation shifts an index value to the right by a variable +amount. The LHS operand is treated as signed. The high order bits are filled +with copies of the most significant bit. If the RHS operand is equal to or +greater than the index bitwidth, the result is a poison value. + +# Example + +```mlir +// c = a >> b +%c = index.shrs %a, %b +``` +""" +function shrs( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.shrs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`shru` + +The `index.shru` operation shifts an index value to the right by a variable +amount. The LHS operand is treated as unsigned. The high order bits are +filled with zeroes. If the RHS operand is equal to or greater than the index +bitwidth, the result is a poison value. + +# Example + +```mlir +// c = a >> b +%c = index.shru %a, %b +``` +""" +function shru( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.shru", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sizeof` + +The `index.sizeof` operation produces an index-typed SSA value equal to the +size in bits of the `index` type. For example, on 32-bit systems, the result +is `32 : index`, and on 64-bit systems, the result is `64 : index`. + +# Example + +```mlir +%0 = index.sizeof +``` +""" +function sizeof(; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.sizeof", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sub` + +The `index.sub` operation takes two index values and computes the difference +of the first from the second operand. + +# Example + +```mlir +// c = a - b +%c = index.sub %a, %b +``` +""" +function sub( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.sub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`xor` + +The `index.xor` operation takes two index values and computes their bitwise +xor. + +# Example + +```mlir +// c = a ^ b +%c = index.xor %a, %b +``` +""" +function xor( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "index.xor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +end # index diff --git a/src/Dialects/19/LLVMIR.jl b/src/Dialects/19/LLVMIR.jl new file mode 100644 index 00000000..652153a4 --- /dev/null +++ b/src/Dialects/19/LLVMIR.jl @@ -0,0 +1,11935 @@ +module llvm + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`ashr` + +""" +function ashr( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.ashr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`add` + +""" +function add( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`addrspacecast` + +""" +function addrspacecast(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.addrspacecast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mlir_addressof` + +Creates an SSA value containing a pointer to a global variable or constant +defined by `llvm.mlir.global`. The global value can be defined after its +first referenced. If the global value is a constant, storing into it is not +allowed. + +Examples: + +```mlir +func @foo() { + // Get the address of a global variable. + %0 = llvm.mlir.addressof @const : !llvm.ptr + + // Use it as a regular pointer. + %1 = llvm.load %0 : !llvm.ptr -> i32 + + // Get the address of a function. + %2 = llvm.mlir.addressof @foo : !llvm.ptr + + // The function address can be used for indirect calls. + llvm.call %2() : !llvm.ptr, () -> () +} + +// Define the global. +llvm.mlir.global @const(42 : i32) : i32 +``` +""" +function mlir_addressof(; res::IR.Type, global_name, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("global_name", global_name),] + + return IR.create_operation( + "llvm.mlir.addressof", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`alloca` + +""" +function alloca( + arraySize::Value; + res::IR.Type, + alignment=nothing, + elem_type, + inalloca=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[arraySize,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("elem_type", elem_type),] + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + !isnothing(inalloca) && push!(_attributes, namedattribute("inalloca", inalloca)) + + return IR.create_operation( + "llvm.alloca", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`and` + +""" +function and( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.and", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cmpxchg` + +""" +function cmpxchg( + ptr::Value, + cmp::Value, + val::Value; + res=nothing::Union{Nothing,IR.Type}, + success_ordering, + failure_ordering, + syncscope=nothing, + alignment=nothing, + weak=nothing, + volatile_=nothing, + access_groups=nothing, + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ptr, cmp, val] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("success_ordering", success_ordering), + namedattribute("failure_ordering", failure_ordering), + ] + !isnothing(res) && push!(_results, res) + !isnothing(syncscope) && push!(_attributes, namedattribute("syncscope", syncscope)) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + !isnothing(weak) && push!(_attributes, namedattribute("weak", weak)) + !isnothing(volatile_) && push!(_attributes, namedattribute("volatile_", volatile_)) + !isnothing(access_groups) && + push!(_attributes, namedattribute("access_groups", access_groups)) + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "llvm.cmpxchg", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`atomicrmw` + +""" +function atomicrmw( + ptr::Value, + val::Value; + res=nothing::Union{Nothing,IR.Type}, + bin_op, + ordering, + syncscope=nothing, + alignment=nothing, + volatile_=nothing, + access_groups=nothing, + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ptr, val] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("bin_op", bin_op), namedattribute("ordering", ordering) + ] + !isnothing(res) && push!(_results, res) + !isnothing(syncscope) && push!(_attributes, namedattribute("syncscope", syncscope)) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + !isnothing(volatile_) && push!(_attributes, namedattribute("volatile_", volatile_)) + !isnothing(access_groups) && + push!(_attributes, namedattribute("access_groups", access_groups)) + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "llvm.atomicrmw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`bitcast` + +""" +function bitcast(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.bitcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`br` + +""" +function br( + destOperands::Vector{Value}; loop_annotation=nothing, dest::Block, location=Location() +) + _results = IR.Type[] + _operands = Value[destOperands...,] + _owned_regions = Region[] + _successors = Block[dest,] + _attributes = NamedAttribute[] + !isnothing(loop_annotation) && + push!(_attributes, namedattribute("loop_annotation", loop_annotation)) + + return IR.create_operation( + "llvm.br", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`call_intrinsic` + +Call the specified llvm intrinsic. If the intrinsic is overloaded, use +the MLIR function type of this op to determine which intrinsic to call. +""" +function call_intrinsic( + args::Vector{Value}; + results=nothing::Union{Nothing,IR.Type}, + intrin, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("intrin", intrin),] + !isnothing(results) && push!(_results, results) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.call_intrinsic", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`call` + +In LLVM IR, functions may return either 0 or 1 value. LLVM IR dialect +implements this behavior by providing a variadic `call` operation for 0- and +1-result functions. Even though MLIR supports multi-result functions, LLVM +IR dialect disallows them. + +The `call` instruction supports both direct and indirect calls. Direct calls +start with a function name (`@`-prefixed) and indirect calls start with an +SSA value (`%`-prefixed). The direct callee, if present, is stored as a +function attribute `callee`. For indirect calls, the callee is of `!llvm.ptr` type +and is stored as the first value in `callee_operands`. If and only if the +callee is a variadic function, the `var_callee_type` attribute must carry +the variadic LLVM function type. The trailing type list contains the +optional indirect callee type and the MLIR function type, which differs from +the LLVM function type that uses an explicit void type to model functions +that do not return a value. + +Examples: + +```mlir +// Direct call without arguments and with one result. +%0 = llvm.call @foo() : () -> (f32) + +// Direct call with arguments and without a result. +llvm.call @bar(%0) : (f32) -> () + +// Indirect call with an argument and without a result. +%1 = llvm.mlir.addressof @foo : !llvm.ptr +llvm.call %1(%0) : !llvm.ptr, (f32) -> () + +// Direct variadic call. +llvm.call @printf(%0, %1) vararg(!llvm.func) : (!llvm.ptr, i32) -> i32 + +// Indirect variadic call +llvm.call %1(%0) vararg(!llvm.func) : !llvm.ptr, (i32) -> () +``` +""" +function call( + callee_operands::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + var_callee_type=nothing, + callee=nothing, + fastmathFlags=nothing, + branch_weights=nothing, + CConv=nothing, + TailCallKind=nothing, + access_groups=nothing, + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[callee_operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(var_callee_type) && + push!(_attributes, namedattribute("var_callee_type", var_callee_type)) + !isnothing(callee) && push!(_attributes, namedattribute("callee", callee)) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + !isnothing(branch_weights) && + push!(_attributes, namedattribute("branch_weights", branch_weights)) + !isnothing(CConv) && push!(_attributes, namedattribute("CConv", CConv)) + !isnothing(TailCallKind) && + push!(_attributes, namedattribute("TailCallKind", TailCallKind)) + !isnothing(access_groups) && + push!(_attributes, namedattribute("access_groups", access_groups)) + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "llvm.call", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`comdat` + +Provides access to object file COMDAT section/group functionality. + +Examples: +```mlir +llvm.comdat @__llvm_comdat { + llvm.comdat_selector @any any +} +llvm.mlir.global internal constant @has_any_comdat(1 : i64) comdat(@__llvm_comdat::@any) : i64 +``` +""" +function comdat(; sym_name, body::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("sym_name", sym_name),] + + return IR.create_operation( + "llvm.comdat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`comdat_selector` + +Provides access to object file COMDAT section/group functionality. + +Examples: +```mlir +llvm.comdat @__llvm_comdat { + llvm.comdat_selector @any any +} +llvm.mlir.global internal constant @has_any_comdat(1 : i64) comdat(@__llvm_comdat::@any) : i64 +``` +""" +function comdat_selector(; sym_name, comdat, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("comdat", comdat) + ] + + return IR.create_operation( + "llvm.comdat_selector", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cond_br` + +""" +function cond_br( + condition::Value, + trueDestOperands::Vector{Value}, + falseDestOperands::Vector{Value}; + branch_weights=nothing, + loop_annotation=nothing, + trueDest::Block, + falseDest::Block, + location=Location(), +) + _results = IR.Type[] + _operands = Value[condition, trueDestOperands..., falseDestOperands...] + _owned_regions = Region[] + _successors = Block[trueDest, falseDest] + _attributes = NamedAttribute[] + push!( + _attributes, + operandsegmentsizes([1, length(trueDestOperands), length(falseDestOperands)]), + ) + !isnothing(branch_weights) && + push!(_attributes, namedattribute("branch_weights", branch_weights)) + !isnothing(loop_annotation) && + push!(_attributes, namedattribute("loop_annotation", loop_annotation)) + + return IR.create_operation( + "llvm.cond_br", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mlir_constant` + +Unlike LLVM IR, MLIR does not have first-class constant values. Therefore, +all constants must be created as SSA values before being used in other +operations. `llvm.mlir.constant` creates such values for scalars and +vectors. It has a mandatory `value` attribute, which may be an integer, +floating point attribute; dense or sparse attribute containing integers or +floats. The type of the attribute is one of the corresponding MLIR builtin +types. It may be omitted for `i64` and `f64` types that are implied. The +operation produces a new SSA value of the specified LLVM IR dialect type. +The type of that value _must_ correspond to the attribute type converted to +LLVM IR. + +Examples: + +```mlir +// Integer constant, internal i32 is mandatory +%0 = llvm.mlir.constant(42 : i32) : i32 + +// It\'s okay to omit i64. +%1 = llvm.mlir.constant(42) : i64 + +// Floating point constant. +%2 = llvm.mlir.constant(42.0 : f32) : f32 + +// Splat dense vector constant. +%3 = llvm.mlir.constant(dense<1.0> : vector<4xf32>) : vector<4xf32> +``` +""" +function mlir_constant(; res::IR.Type, value, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "llvm.mlir.constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`extractelement` + +""" +function extractelement( + vector::Value, position::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[vector, position] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.extractelement", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`extractvalue` + +""" +function extractvalue(container::Value; res::IR.Type, position, location=Location()) + _results = IR.Type[res,] + _operands = Value[container,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("position", position),] + + return IR.create_operation( + "llvm.extractvalue", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fadd` + +""" +function fadd( + lhs::Value, + rhs::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.fadd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`fcmp` + +""" +function fcmp( + lhs::Value, + rhs::Value; + res=nothing::Union{Nothing,IR.Type}, + predicate, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("predicate", predicate),] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.fcmp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`fdiv` + +""" +function fdiv( + lhs::Value, + rhs::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.fdiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`fmul` + +""" +function fmul( + lhs::Value, + rhs::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.fmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`fneg` + +""" +function fneg( + operand::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.fneg", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`fpext` + +""" +function fpext(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.fpext", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fptosi` + +""" +function fptosi(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.fptosi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fptoui` + +""" +function fptoui(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.fptoui", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fptrunc` + +""" +function fptrunc(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.fptrunc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`frem` + +""" +function frem( + lhs::Value, + rhs::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.frem", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`fsub` + +""" +function fsub( + lhs::Value, + rhs::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.fsub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`fence` + +""" +function fence(; ordering, syncscope=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("ordering", ordering),] + !isnothing(syncscope) && push!(_attributes, namedattribute("syncscope", syncscope)) + + return IR.create_operation( + "llvm.fence", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`freeze` + +""" +function freeze(val::Value; res=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[val,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.freeze", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`getelementptr` + +This operation mirrors LLVM IRs \'getelementptr\' operation that is used to +perform pointer arithmetic. + +Like in LLVM IR, it is possible to use both constants as well as SSA values +as indices. In the case of indexing within a structure, it is required to +either use constant indices directly, or supply a constant SSA value. + +An optional \'inbounds\' attribute specifies the low-level pointer arithmetic +overflow behavior that LLVM uses after lowering the operation to LLVM IR. + +Examples: + +```mlir +// GEP with an SSA value offset +%0 = llvm.getelementptr %1[%2] : (!llvm.ptr, i64) -> !llvm.ptr, f32 + +// GEP with a constant offset and the inbounds attribute set +%0 = llvm.getelementptr inbounds %1[3] : (!llvm.ptr) -> !llvm.ptr, f32 + +// GEP with constant offsets into a structure +%0 = llvm.getelementptr %1[0, 1] + : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i32, f32)> +``` +""" +function getelementptr( + base::Value, + dynamicIndices::Vector{Value}; + res::IR.Type, + rawConstantIndices, + elem_type, + inbounds=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[base, dynamicIndices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("rawConstantIndices", rawConstantIndices), + namedattribute("elem_type", elem_type), + ] + !isnothing(inbounds) && push!(_attributes, namedattribute("inbounds", inbounds)) + + return IR.create_operation( + "llvm.getelementptr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mlir_global_ctors` + +Specifies a list of constructor functions and priorities. The functions +referenced by this array will be called in ascending order of priority (i.e. +lowest first) when the module is loaded. The order of functions with the +same priority is not defined. This operation is translated to LLVM\'s +global_ctors global variable. The initializer functions are run at load +time. The `data` field present in LLVM\'s global_ctors variable is not +modeled here. + +Examples: + +```mlir +llvm.mlir.global_ctors {@ctor} + +llvm.func @ctor() { + ... + llvm.return +} +``` +""" +function mlir_global_ctors(; ctors, priorities, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("ctors", ctors), namedattribute("priorities", priorities) + ] + + return IR.create_operation( + "llvm.mlir.global_ctors", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mlir_global_dtors` + +Specifies a list of destructor functions and priorities. The functions +referenced by this array will be called in descending order of priority (i.e. +highest first) when the module is unloaded. The order of functions with the +same priority is not defined. This operation is translated to LLVM\'s +global_dtors global variable. The `data` field present in LLVM\'s +global_dtors variable is not modeled here. + +Examples: + +```mlir +llvm.func @dtor() { + llvm.return +} +llvm.mlir.global_dtors {@dtor} +``` +""" +function mlir_global_dtors(; dtors, priorities, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("dtors", dtors), namedattribute("priorities", priorities) + ] + + return IR.create_operation( + "llvm.mlir.global_dtors", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mlir_global` + +Since MLIR allows for arbitrary operations to be present at the top level, +global variables are defined using the `llvm.mlir.global` operation. Both +global constants and variables can be defined, and the value may also be +initialized in both cases. + +There are two forms of initialization syntax. Simple constants that can be +represented as MLIR attributes can be given in-line: + +```mlir +llvm.mlir.global @variable(32.0 : f32) : f32 +``` + +This initialization and type syntax is similar to `llvm.mlir.constant` and +may use two types: one for MLIR attribute and another for the LLVM value. +These types must be compatible. + +More complex constants that cannot be represented as MLIR attributes can be +given in an initializer region: + +```mlir +// This global is initialized with the equivalent of: +// i32* getelementptr (i32* @g2, i32 2) +llvm.mlir.global constant @int_gep() : !llvm.ptr { + %0 = llvm.mlir.addressof @g2 : !llvm.ptr + %1 = llvm.mlir.constant(2 : i32) : i32 + %2 = llvm.getelementptr %0[%1] + : (!llvm.ptr, i32) -> !llvm.ptr, i32 + // The initializer region must end with `llvm.return`. + llvm.return %2 : !llvm.ptr +} +``` + +Only one of the initializer attribute or initializer region may be provided. + +`llvm.mlir.global` must appear at top-level of the enclosing module. It uses +an @-identifier for its value, which will be uniqued by the module with +respect to other @-identifiers in it. + +Examples: + +```mlir +// Global values use @-identifiers. +llvm.mlir.global constant @cst(42 : i32) : i32 + +// Non-constant values must also be initialized. +llvm.mlir.global @variable(32.0 : f32) : f32 + +// Strings are expected to be of wrapped LLVM i8 array type and do not +// automatically include the trailing zero. +llvm.mlir.global @string(\"abc\") : !llvm.array<3 x i8> + +// For strings globals, the trailing type may be omitted. +llvm.mlir.global constant @no_trailing_type(\"foo bar\") + +// A complex initializer is constructed with an initializer region. +llvm.mlir.global constant @int_gep() : !llvm.ptr { + %0 = llvm.mlir.addressof @g2 : !llvm.ptr + %1 = llvm.mlir.constant(2 : i32) : i32 + %2 = llvm.getelementptr %0[%1] + : (!llvm.ptr, i32) -> !llvm.ptr, i32 + llvm.return %2 : !llvm.ptr +} +``` + +Similarly to functions, globals have a linkage attribute. In the custom +syntax, this attribute is placed between `llvm.mlir.global` and the optional +`constant` keyword. If the attribute is omitted, `external` linkage is +assumed by default. + +Examples: + +```mlir +// A constant with internal linkage will not participate in linking. +llvm.mlir.global internal constant @cst(42 : i32) : i32 + +// By default, \"external\" linkage is assumed and the global participates in +// symbol resolution at link-time. +llvm.mlir.global @glob(0 : f32) : f32 + +// Alignment is optional +llvm.mlir.global private constant @y(dense<1.0> : tensor<8xf32>) : !llvm.array<8 x f32> +``` + +Like global variables in LLVM IR, globals can have an (optional) +alignment attribute using keyword `alignment`. The integer value of the +alignment must be a positive integer that is a power of 2. + +Examples: + +```mlir +// Alignment is optional +llvm.mlir.global private constant @y(dense<1.0> : tensor<8xf32>) { alignment = 32 : i64 } : !llvm.array<8 x f32> +``` +""" +function mlir_global(; + global_type, + constant=nothing, + sym_name, + linkage, + dso_local=nothing, + thread_local_=nothing, + value=nothing, + alignment=nothing, + addr_space=nothing, + unnamed_addr=nothing, + section=nothing, + comdat=nothing, + dbg_expr=nothing, + visibility_=nothing, + initializer::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[initializer,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("global_type", global_type), + namedattribute("sym_name", sym_name), + namedattribute("linkage", linkage), + ] + !isnothing(constant) && push!(_attributes, namedattribute("constant", constant)) + !isnothing(dso_local) && push!(_attributes, namedattribute("dso_local", dso_local)) + !isnothing(thread_local_) && + push!(_attributes, namedattribute("thread_local_", thread_local_)) + !isnothing(value) && push!(_attributes, namedattribute("value", value)) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + !isnothing(addr_space) && push!(_attributes, namedattribute("addr_space", addr_space)) + !isnothing(unnamed_addr) && + push!(_attributes, namedattribute("unnamed_addr", unnamed_addr)) + !isnothing(section) && push!(_attributes, namedattribute("section", section)) + !isnothing(comdat) && push!(_attributes, namedattribute("comdat", comdat)) + !isnothing(dbg_expr) && push!(_attributes, namedattribute("dbg_expr", dbg_expr)) + !isnothing(visibility_) && + push!(_attributes, namedattribute("visibility_", visibility_)) + + return IR.create_operation( + "llvm.mlir.global", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`icmp` + +""" +function icmp( + lhs::Value, + rhs::Value; + res=nothing::Union{Nothing,IR.Type}, + predicate, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("predicate", predicate),] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.icmp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`inline_asm` + +The InlineAsmOp mirrors the underlying LLVM semantics with a notable +exception: the embedded `asm_string` is not allowed to define or reference +any symbol or any global variable: only the operands of the op may be read, +written, or referenced. +Attempting to define or reference any symbol or any global behavior is +considered undefined behavior at this time. +""" +function inline_asm( + operands::Vector{Value}; + res=nothing::Union{Nothing,IR.Type}, + asm_string, + constraints, + has_side_effects=nothing, + is_align_stack=nothing, + asm_dialect=nothing, + operand_attrs=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("asm_string", asm_string), namedattribute("constraints", constraints) + ] + !isnothing(res) && push!(_results, res) + !isnothing(has_side_effects) && + push!(_attributes, namedattribute("has_side_effects", has_side_effects)) + !isnothing(is_align_stack) && + push!(_attributes, namedattribute("is_align_stack", is_align_stack)) + !isnothing(asm_dialect) && + push!(_attributes, namedattribute("asm_dialect", asm_dialect)) + !isnothing(operand_attrs) && + push!(_attributes, namedattribute("operand_attrs", operand_attrs)) + + return IR.create_operation( + "llvm.inline_asm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`insertelement` + +""" +function insertelement( + vector::Value, + value::Value, + position::Value; + res=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vector, value, position] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.insertelement", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`insertvalue` + +""" +function insertvalue( + container::Value, + value::Value; + res=nothing::Union{Nothing,IR.Type}, + position, + location=Location(), +) + _results = IR.Type[] + _operands = Value[container, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("position", position),] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.insertvalue", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`inttoptr` + +""" +function inttoptr(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.inttoptr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`invoke` + +""" +function invoke( + callee_operands::Vector{Value}, + normalDestOperands::Vector{Value}, + unwindDestOperands::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + var_callee_type=nothing, + callee=nothing, + branch_weights=nothing, + CConv=nothing, + normalDest::Block, + unwindDest::Block, + location=Location(), +) + _results = IR.Type[] + _operands = Value[callee_operands..., normalDestOperands..., unwindDestOperands...] + _owned_regions = Region[] + _successors = Block[normalDest, unwindDest] + _attributes = NamedAttribute[] + push!( + _attributes, + operandsegmentsizes([ + length(callee_operands), length(normalDestOperands), length(unwindDestOperands) + ]), + ) + !isnothing(result) && push!(_results, result) + !isnothing(var_callee_type) && + push!(_attributes, namedattribute("var_callee_type", var_callee_type)) + !isnothing(callee) && push!(_attributes, namedattribute("callee", callee)) + !isnothing(branch_weights) && + push!(_attributes, namedattribute("branch_weights", branch_weights)) + !isnothing(CConv) && push!(_attributes, namedattribute("CConv", CConv)) + + return IR.create_operation( + "llvm.invoke", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`func` + +MLIR functions are defined by an operation that is not built into the IR +itself. The LLVM dialect provides an `llvm.func` operation to define +functions compatible with LLVM IR. These functions have LLVM dialect +function type but use MLIR syntax to express it. They are required to have +exactly one result type. LLVM function operation is intended to capture +additional properties of LLVM functions, such as linkage and calling +convention, that may be modeled differently by the built-in MLIR function. + +```mlir +// The type of @bar is !llvm<\"i64 (i64)\"> +llvm.func @bar(%arg0: i64) -> i64 { + llvm.return %arg0 : i64 +} + +// Type type of @foo is !llvm<\"void (i64)\"> +// !llvm.void type is omitted +llvm.func @foo(%arg0: i64) { + llvm.return +} + +// A function with `internal` linkage. +llvm.func internal @internal_func() { + llvm.return +} +``` +""" +function func(; + sym_name, + sym_visibility=nothing, + function_type, + linkage=nothing, + dso_local=nothing, + CConv=nothing, + comdat=nothing, + convergent=nothing, + personality=nothing, + garbageCollector=nothing, + passthrough=nothing, + arg_attrs=nothing, + res_attrs=nothing, + function_entry_count=nothing, + memory=nothing, + visibility_=nothing, + arm_streaming=nothing, + arm_locally_streaming=nothing, + arm_streaming_compatible=nothing, + arm_new_za=nothing, + arm_in_za=nothing, + arm_out_za=nothing, + arm_inout_za=nothing, + arm_preserves_za=nothing, + section=nothing, + unnamed_addr=nothing, + alignment=nothing, + vscale_range=nothing, + frame_pointer=nothing, + target_cpu=nothing, + tune_cpu=nothing, + target_features=nothing, + unsafe_fp_math=nothing, + no_infs_fp_math=nothing, + no_nans_fp_math=nothing, + approx_func_fp_math=nothing, + no_signed_zeros_fp_math=nothing, + denormal_fp_math=nothing, + denormal_fp_math_f32=nothing, + fp_contract=nothing, + no_inline=nothing, + always_inline=nothing, + no_unwind=nothing, + will_return=nothing, + optimize_none=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("function_type", function_type) + ] + !isnothing(sym_visibility) && + push!(_attributes, namedattribute("sym_visibility", sym_visibility)) + !isnothing(linkage) && push!(_attributes, namedattribute("linkage", linkage)) + !isnothing(dso_local) && push!(_attributes, namedattribute("dso_local", dso_local)) + !isnothing(CConv) && push!(_attributes, namedattribute("CConv", CConv)) + !isnothing(comdat) && push!(_attributes, namedattribute("comdat", comdat)) + !isnothing(convergent) && push!(_attributes, namedattribute("convergent", convergent)) + !isnothing(personality) && + push!(_attributes, namedattribute("personality", personality)) + !isnothing(garbageCollector) && + push!(_attributes, namedattribute("garbageCollector", garbageCollector)) + !isnothing(passthrough) && + push!(_attributes, namedattribute("passthrough", passthrough)) + !isnothing(arg_attrs) && push!(_attributes, namedattribute("arg_attrs", arg_attrs)) + !isnothing(res_attrs) && push!(_attributes, namedattribute("res_attrs", res_attrs)) + !isnothing(function_entry_count) && + push!(_attributes, namedattribute("function_entry_count", function_entry_count)) + !isnothing(memory) && push!(_attributes, namedattribute("memory", memory)) + !isnothing(visibility_) && + push!(_attributes, namedattribute("visibility_", visibility_)) + !isnothing(arm_streaming) && + push!(_attributes, namedattribute("arm_streaming", arm_streaming)) + !isnothing(arm_locally_streaming) && + push!(_attributes, namedattribute("arm_locally_streaming", arm_locally_streaming)) + !isnothing(arm_streaming_compatible) && push!( + _attributes, + namedattribute("arm_streaming_compatible", arm_streaming_compatible), + ) + !isnothing(arm_new_za) && push!(_attributes, namedattribute("arm_new_za", arm_new_za)) + !isnothing(arm_in_za) && push!(_attributes, namedattribute("arm_in_za", arm_in_za)) + !isnothing(arm_out_za) && push!(_attributes, namedattribute("arm_out_za", arm_out_za)) + !isnothing(arm_inout_za) && + push!(_attributes, namedattribute("arm_inout_za", arm_inout_za)) + !isnothing(arm_preserves_za) && + push!(_attributes, namedattribute("arm_preserves_za", arm_preserves_za)) + !isnothing(section) && push!(_attributes, namedattribute("section", section)) + !isnothing(unnamed_addr) && + push!(_attributes, namedattribute("unnamed_addr", unnamed_addr)) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + !isnothing(vscale_range) && + push!(_attributes, namedattribute("vscale_range", vscale_range)) + !isnothing(frame_pointer) && + push!(_attributes, namedattribute("frame_pointer", frame_pointer)) + !isnothing(target_cpu) && push!(_attributes, namedattribute("target_cpu", target_cpu)) + !isnothing(tune_cpu) && push!(_attributes, namedattribute("tune_cpu", tune_cpu)) + !isnothing(target_features) && + push!(_attributes, namedattribute("target_features", target_features)) + !isnothing(unsafe_fp_math) && + push!(_attributes, namedattribute("unsafe_fp_math", unsafe_fp_math)) + !isnothing(no_infs_fp_math) && + push!(_attributes, namedattribute("no_infs_fp_math", no_infs_fp_math)) + !isnothing(no_nans_fp_math) && + push!(_attributes, namedattribute("no_nans_fp_math", no_nans_fp_math)) + !isnothing(approx_func_fp_math) && + push!(_attributes, namedattribute("approx_func_fp_math", approx_func_fp_math)) + !isnothing(no_signed_zeros_fp_math) && push!( + _attributes, namedattribute("no_signed_zeros_fp_math", no_signed_zeros_fp_math) + ) + !isnothing(denormal_fp_math) && + push!(_attributes, namedattribute("denormal_fp_math", denormal_fp_math)) + !isnothing(denormal_fp_math_f32) && + push!(_attributes, namedattribute("denormal_fp_math_f32", denormal_fp_math_f32)) + !isnothing(fp_contract) && + push!(_attributes, namedattribute("fp_contract", fp_contract)) + !isnothing(no_inline) && push!(_attributes, namedattribute("no_inline", no_inline)) + !isnothing(always_inline) && + push!(_attributes, namedattribute("always_inline", always_inline)) + !isnothing(no_unwind) && push!(_attributes, namedattribute("no_unwind", no_unwind)) + !isnothing(will_return) && + push!(_attributes, namedattribute("will_return", will_return)) + !isnothing(optimize_none) && + push!(_attributes, namedattribute("optimize_none", optimize_none)) + + return IR.create_operation( + "llvm.func", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`lshr` + +""" +function lshr( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.lshr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`landingpad` + +""" +function landingpad( + operand_0::Vector{Value}; res::IR.Type, cleanup=nothing, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(cleanup) && push!(_attributes, namedattribute("cleanup", cleanup)) + + return IR.create_operation( + "llvm.landingpad", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`linker_options` + +Pass the given options to the linker when the resulting object file is linked. +This is used extensively on Windows to determine the C runtime that the object +files should link against. + +Examples: +```mlir +// Link against the MSVC static threaded CRT. +llvm.linker_options [\"/DEFAULTLIB:\", \"libcmt\"] + +// Link against aarch64 compiler-rt builtins +llvm.linker_options [\"-l\", \"clang_rt.builtins-aarch64\"] +``` +""" +function linker_options(; options, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("options", options),] + + return IR.create_operation( + "llvm.linker_options", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`load` + +The `load` operation is used to read from memory. A load may be marked as +atomic, volatile, and/or nontemporal, and takes a number of optional +attributes that specify aliasing information. + +An atomic load only supports a limited set of pointer, integer, and +floating point types, and requires an explicit alignment. + +Examples: +```mlir +// A volatile load of a float variable. +%0 = llvm.load volatile %ptr : !llvm.ptr -> f32 + +// A nontemporal load of a float variable. +%0 = llvm.load %ptr {nontemporal} : !llvm.ptr -> f32 + +// An atomic load of an integer variable. +%0 = llvm.load %ptr atomic monotonic {alignment = 8 : i64} + : !llvm.ptr -> i64 +``` + +See the following link for more details: +https://llvm.org/docs/LangRef.html#load-instruction +""" +function load( + addr::Value; + res::IR.Type, + alignment=nothing, + volatile_=nothing, + nontemporal=nothing, + invariant=nothing, + ordering=nothing, + syncscope=nothing, + access_groups=nothing, + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[addr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + !isnothing(volatile_) && push!(_attributes, namedattribute("volatile_", volatile_)) + !isnothing(nontemporal) && + push!(_attributes, namedattribute("nontemporal", nontemporal)) + !isnothing(invariant) && push!(_attributes, namedattribute("invariant", invariant)) + !isnothing(ordering) && push!(_attributes, namedattribute("ordering", ordering)) + !isnothing(syncscope) && push!(_attributes, namedattribute("syncscope", syncscope)) + !isnothing(access_groups) && + push!(_attributes, namedattribute("access_groups", access_groups)) + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "llvm.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mul` + +""" +function mul( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mlir_none` + +Unlike LLVM IR, MLIR does not have first-class token values. They must be +explicitly created as SSA values using `llvm.mlir.none`. This operation has +no operands or attributes, and returns a none token value of a wrapped LLVM IR +pointer type. + +Examples: + +```mlir +%0 = llvm.mlir.none : !llvm.token +``` +""" +function mlir_none(; res=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.mlir.none", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`or` + +""" +function or( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.or", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mlir_poison` + +Unlike LLVM IR, MLIR does not have first-class poison values. Such values +must be created as SSA values using `llvm.mlir.poison`. This operation has +no operands or attributes. It creates a poison value of the specified LLVM +IR dialect type. + +# Example + +```mlir +// Create a poison value for a structure with a 32-bit integer followed +// by a float. +%0 = llvm.mlir.poison : !llvm.struct<(i32, f32)> +``` +""" +function mlir_poison(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.mlir.poison", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ptrtoint` + +""" +function ptrtoint(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.ptrtoint", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`resume` + +""" +function resume(value::Value; location=Location()) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.resume", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`return_` + +""" +function return_(arg=nothing::Union{Nothing,Value}; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(arg) && push!(_operands, arg) + + return IR.create_operation( + "llvm.return", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sdiv` + +""" +function sdiv( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.sdiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sext` + +""" +function sext(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.sext", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sitofp` + +""" +function sitofp(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.sitofp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`srem` + +""" +function srem( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.srem", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`select` + +""" +function select( + condition::Value, + trueValue::Value, + falseValue::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[condition, trueValue, falseValue] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.select", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`shl` + +""" +function shl( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.shl", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`shufflevector` + +""" +function shufflevector(v1::Value, v2::Value; res::IR.Type, mask, location=Location()) + _results = IR.Type[res,] + _operands = Value[v1, v2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mask", mask),] + + return IR.create_operation( + "llvm.shufflevector", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`store` + +The `store` operation is used to write to memory. A store may be marked as +atomic, volatile, and/or nontemporal, and takes a number of optional +attributes that specify aliasing information. + +An atomic store only supports a limited set of pointer, integer, and +floating point types, and requires an explicit alignment. + +Examples: +```mlir +// A volatile store of a float variable. +llvm.store volatile %val, %ptr : f32, !llvm.ptr + +// A nontemporal store of a float variable. +llvm.store %val, %ptr {nontemporal} : f32, !llvm.ptr + +// An atomic store of an integer variable. +llvm.store %val, %ptr atomic monotonic {alignment = 8 : i64} + : i64, !llvm.ptr +``` + +See the following link for more details: +https://llvm.org/docs/LangRef.html#store-instruction +""" +function store( + value::Value, + addr::Value; + alignment=nothing, + volatile_=nothing, + nontemporal=nothing, + ordering=nothing, + syncscope=nothing, + access_groups=nothing, + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, addr] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + !isnothing(volatile_) && push!(_attributes, namedattribute("volatile_", volatile_)) + !isnothing(nontemporal) && + push!(_attributes, namedattribute("nontemporal", nontemporal)) + !isnothing(ordering) && push!(_attributes, namedattribute("ordering", ordering)) + !isnothing(syncscope) && push!(_attributes, namedattribute("syncscope", syncscope)) + !isnothing(access_groups) && + push!(_attributes, namedattribute("access_groups", access_groups)) + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "llvm.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sub` + +""" +function sub( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.sub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`switch` + +""" +function switch( + value::Value, + defaultOperands::Vector{Value}, + caseOperands::Vector{Value}; + case_values=nothing, + case_operand_segments, + branch_weights=nothing, + defaultDestination::Block, + caseDestinations::Vector{Block}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, defaultOperands..., caseOperands...] + _owned_regions = Region[] + _successors = Block[defaultDestination, caseDestinations...] + _attributes = NamedAttribute[namedattribute( + "case_operand_segments", case_operand_segments + ),] + push!( + _attributes, operandsegmentsizes([1, length(defaultOperands), length(caseOperands)]) + ) + !isnothing(case_values) && + push!(_attributes, namedattribute("case_values", case_values)) + !isnothing(branch_weights) && + push!(_attributes, namedattribute("branch_weights", branch_weights)) + + return IR.create_operation( + "llvm.switch", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`trunc` + +""" +function trunc(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.trunc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`udiv` + +""" +function udiv( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.udiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`uitofp` + +""" +function uitofp(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.uitofp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`urem` + +""" +function urem( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.urem", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mlir_undef` + +Unlike LLVM IR, MLIR does not have first-class undefined values. Such values +must be created as SSA values using `llvm.mlir.undef`. This operation has no +operands or attributes. It creates an undefined value of the specified LLVM +IR dialect type. + +# Example + +```mlir +// Create a structure with a 32-bit integer followed by a float. +%0 = llvm.mlir.undef : !llvm.struct<(i32, f32)> +``` +""" +function mlir_undef(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.mlir.undef", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`unreachable` + +""" +function unreachable(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.unreachable", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`xor` + +""" +function xor( + lhs::Value, rhs::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.xor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`zext` + +""" +function zext(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.zext", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mlir_zero` + +Unlike LLVM IR, MLIR does not have first-class zero-initialized values. +Such values must be created as SSA values using `llvm.mlir.zero`. This +operation has no operands or attributes. It creates a zero-initialized +value of the specified LLVM IR dialect type. + +# Example + +```mlir +// Create a zero-initialized value for a structure with a 32-bit integer +// followed by a float. +%0 = llvm.mlir.zero : !llvm.struct<(i32, f32)> +``` +""" +function mlir_zero(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.mlir.zero", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`intr_abs` + +""" +function intr_abs(in::Value; res::IR.Type, is_int_min_poison, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("is_int_min_poison", is_int_min_poison),] + + return IR.create_operation( + "llvm.intr.abs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_annotation` + +""" +function intr_annotation( + integer::Value, + annotation::Value, + fileName::Value, + line::Value; + res=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[integer, annotation, fileName, line] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.annotation", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_assume` + +""" +function intr_assume(cond::Value; location=Location()) + _results = IR.Type[] + _operands = Value[cond,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.assume", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_bitreverse` + +""" +function intr_bitreverse( + in::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.bitreverse", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_bswap` + +""" +function intr_bswap(in::Value; res=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.bswap", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_experimental_constrained_fptrunc` + +""" +function intr_experimental_constrained_fptrunc( + arg_0::Value; res::IR.Type, roundingmode, fpExceptionBehavior, location=Location() +) + _results = IR.Type[res,] + _operands = Value[arg_0,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("roundingmode", roundingmode), + namedattribute("fpExceptionBehavior", fpExceptionBehavior), + ] + + return IR.create_operation( + "llvm.intr.experimental.constrained.fptrunc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_copysign` + +""" +function intr_copysign( + a::Value, + b::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.copysign", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_coro_align` + +""" +function intr_coro_align(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.coro.align", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_coro_begin` + +""" +function intr_coro_begin(token::Value, mem::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[token, mem] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.coro.begin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_coro_end` + +""" +function intr_coro_end( + handle::Value, unwind::Value, retvals::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[handle, unwind, retvals] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.coro.end", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_coro_free` + +""" +function intr_coro_free(id::Value, handle::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[id, handle] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.coro.free", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_coro_id` + +""" +function intr_coro_id( + align::Value, + promise::Value, + coroaddr::Value, + fnaddrs::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[align, promise, coroaddr, fnaddrs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.coro.id", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_coro_promise` + +""" +function intr_coro_promise( + handle::Value, align::Value, from::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[handle, align, from] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.coro.promise", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_coro_resume` + +""" +function intr_coro_resume(handle::Value; location=Location()) + _results = IR.Type[] + _operands = Value[handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.coro.resume", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_coro_save` + +""" +function intr_coro_save(handle::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.coro.save", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_coro_size` + +""" +function intr_coro_size(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.coro.size", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_coro_suspend` + +""" +function intr_coro_suspend(save::Value, final::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[save, final] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.coro.suspend", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_cos` + +""" +function intr_cos( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.cos", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_ctlz` + +""" +function intr_ctlz(in::Value; res::IR.Type, is_zero_poison, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("is_zero_poison", is_zero_poison),] + + return IR.create_operation( + "llvm.intr.ctlz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_cttz` + +""" +function intr_cttz(in::Value; res::IR.Type, is_zero_poison, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("is_zero_poison", is_zero_poison),] + + return IR.create_operation( + "llvm.intr.cttz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ctpop` + +""" +function intr_ctpop(in::Value; res=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.ctpop", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_dbg_declare` + +""" +function intr_dbg_declare(addr::Value; varInfo, locationExpr=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[addr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("varInfo", varInfo),] + !isnothing(locationExpr) && + push!(_attributes, namedattribute("locationExpr", locationExpr)) + + return IR.create_operation( + "llvm.intr.dbg.declare", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_dbg_label` + +""" +function intr_dbg_label(; label, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("label", label),] + + return IR.create_operation( + "llvm.intr.dbg.label", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_dbg_value` + +""" +function intr_dbg_value(value::Value; varInfo, locationExpr=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("varInfo", varInfo),] + !isnothing(locationExpr) && + push!(_attributes, namedattribute("locationExpr", locationExpr)) + + return IR.create_operation( + "llvm.intr.dbg.value", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_debugtrap` + +""" +function intr_debugtrap(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.debugtrap", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_eh_typeid_for` + +""" +function intr_eh_typeid_for(type_info::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[type_info,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.eh.typeid.for", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_exp2` + +""" +function intr_exp2( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.exp2", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_exp` + +""" +function intr_exp( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.exp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_expect` + +""" +function intr_expect( + val::Value, expected::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[val, expected] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.expect", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_expect_with_probability` + +""" +function intr_expect_with_probability( + val::Value, + expected::Value; + res=nothing::Union{Nothing,IR.Type}, + prob, + location=Location(), +) + _results = IR.Type[] + _operands = Value[val, expected] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("prob", prob),] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.expect.with.probability", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_fabs` + +""" +function intr_fabs( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.fabs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_ceil` + +""" +function intr_ceil( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.ceil", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_floor` + +""" +function intr_floor( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.floor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_fma` + +""" +function intr_fma( + a::Value, + b::Value, + c::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b, c] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.fma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_fmuladd` + +""" +function intr_fmuladd( + a::Value, + b::Value, + c::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b, c] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.fmuladd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_trunc` + +""" +function intr_trunc( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.trunc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_fshl` + +""" +function intr_fshl( + a::Value, b::Value, c::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b, c] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.fshl", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_fshr` + +""" +function intr_fshr( + a::Value, b::Value, c::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b, c] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.fshr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_get_active_lane_mask` + +""" +function intr_get_active_lane_mask(base::Value, n::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[base, n] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.get.active.lane.mask", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_invariant_end` + +""" +function intr_invariant_end(start::Value, ptr::Value; size, location=Location()) + _results = IR.Type[] + _operands = Value[start, ptr] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("size", size),] + + return IR.create_operation( + "llvm.intr.invariant.end", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_invariant_start` + +""" +function intr_invariant_start( + ptr::Value; res=nothing::Union{Nothing,IR.Type}, size, location=Location() +) + _results = IR.Type[] + _operands = Value[ptr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("size", size),] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.invariant.start", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_is_constant` + +""" +function intr_is_constant( + val::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[val,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.is.constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_is_fpclass` + +""" +function intr_is_fpclass(in::Value; res::IR.Type, bit, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("bit", bit),] + + return IR.create_operation( + "llvm.intr.is.fpclass", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_lifetime_end` + +""" +function intr_lifetime_end(ptr::Value; size, location=Location()) + _results = IR.Type[] + _operands = Value[ptr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("size", size),] + + return IR.create_operation( + "llvm.intr.lifetime.end", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_lifetime_start` + +""" +function intr_lifetime_start(ptr::Value; size, location=Location()) + _results = IR.Type[] + _operands = Value[ptr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("size", size),] + + return IR.create_operation( + "llvm.intr.lifetime.start", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_llrint` + +""" +function intr_llrint(val::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[val,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.llrint", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_llround` + +""" +function intr_llround(val::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[val,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.llround", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_log2` + +""" +function intr_log2( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.log2", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_log10` + +""" +function intr_log10( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.log10", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_log` + +""" +function intr_log( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.log", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_lrint` + +""" +function intr_lrint(val::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[val,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.lrint", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_lround` + +""" +function intr_lround(val::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[val,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.lround", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_masked_load` + +""" +function intr_masked_load( + data::Value, + mask::Value, + pass_thru::Vector{Value}; + res::IR.Type, + alignment, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[data, mask, pass_thru...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("alignment", alignment),] + + return IR.create_operation( + "llvm.intr.masked.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_masked_store` + +""" +function intr_masked_store( + value::Value, data::Value, mask::Value; alignment, location=Location() +) + _results = IR.Type[] + _operands = Value[value, data, mask] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("alignment", alignment),] + + return IR.create_operation( + "llvm.intr.masked.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_matrix_column_major_load` + +""" +function intr_matrix_column_major_load( + data::Value, stride::Value; res::IR.Type, isVolatile, rows, columns, location=Location() +) + _results = IR.Type[res,] + _operands = Value[data, stride] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("isVolatile", isVolatile), + namedattribute("rows", rows), + namedattribute("columns", columns), + ] + + return IR.create_operation( + "llvm.intr.matrix.column.major.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_matrix_column_major_store` + +""" +function intr_matrix_column_major_store( + matrix::Value, + data::Value, + stride::Value; + isVolatile, + rows, + columns, + location=Location(), +) + _results = IR.Type[] + _operands = Value[matrix, data, stride] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("isVolatile", isVolatile), + namedattribute("rows", rows), + namedattribute("columns", columns), + ] + + return IR.create_operation( + "llvm.intr.matrix.column.major.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_matrix_multiply` + +""" +function intr_matrix_multiply( + lhs::Value, + rhs::Value; + res::IR.Type, + lhs_rows, + lhs_columns, + rhs_columns, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("lhs_rows", lhs_rows), + namedattribute("lhs_columns", lhs_columns), + namedattribute("rhs_columns", rhs_columns), + ] + + return IR.create_operation( + "llvm.intr.matrix.multiply", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_matrix_transpose` + +""" +function intr_matrix_transpose( + matrix::Value; res::IR.Type, rows, columns, location=Location() +) + _results = IR.Type[res,] + _operands = Value[matrix,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("rows", rows), namedattribute("columns", columns) + ] + + return IR.create_operation( + "llvm.intr.matrix.transpose", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_maxnum` + +""" +function intr_maxnum( + a::Value, + b::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.maxnum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_maximum` + +""" +function intr_maximum( + a::Value, + b::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.maximum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_memcpy_inline` + +""" +function intr_memcpy_inline( + dst::Value, + src::Value; + len, + isVolatile, + access_groups=nothing, + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[dst, src] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("len", len), namedattribute("isVolatile", isVolatile) + ] + !isnothing(access_groups) && + push!(_attributes, namedattribute("access_groups", access_groups)) + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "llvm.intr.memcpy.inline", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_memcpy` + +""" +function intr_memcpy( + dst::Value, + src::Value, + len::Value; + isVolatile, + access_groups=nothing, + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[dst, src, len] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("isVolatile", isVolatile),] + !isnothing(access_groups) && + push!(_attributes, namedattribute("access_groups", access_groups)) + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "llvm.intr.memcpy", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_memmove` + +""" +function intr_memmove( + dst::Value, + src::Value, + len::Value; + isVolatile, + access_groups=nothing, + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[dst, src, len] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("isVolatile", isVolatile),] + !isnothing(access_groups) && + push!(_attributes, namedattribute("access_groups", access_groups)) + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "llvm.intr.memmove", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_memset` + +""" +function intr_memset( + dst::Value, + val::Value, + len::Value; + isVolatile, + access_groups=nothing, + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[dst, val, len] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("isVolatile", isVolatile),] + !isnothing(access_groups) && + push!(_attributes, namedattribute("access_groups", access_groups)) + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "llvm.intr.memset", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_minnum` + +""" +function intr_minnum( + a::Value, + b::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.minnum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_minimum` + +""" +function intr_minimum( + a::Value, + b::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.minimum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_nearbyint` + +""" +function intr_nearbyint( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.nearbyint", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_experimental_noalias_scope_decl` + +""" +function intr_experimental_noalias_scope_decl(; scope, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("scope", scope),] + + return IR.create_operation( + "llvm.intr.experimental.noalias.scope.decl", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_powi` + +""" +function intr_powi( + val::Value, power::Value; res::IR.Type, fastmathFlags=nothing, location=Location() +) + _results = IR.Type[res,] + _operands = Value[val, power] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.powi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_pow` + +""" +function intr_pow( + a::Value, + b::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.pow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_prefetch` + +""" +function intr_prefetch(addr::Value; rw, hint, cache, location=Location()) + _results = IR.Type[] + _operands = Value[addr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("rw", rw), + namedattribute("hint", hint), + namedattribute("cache", cache), + ] + + return IR.create_operation( + "llvm.intr.prefetch", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ptr_annotation` + +""" +function intr_ptr_annotation( + ptr::Value, + annotation::Value, + fileName::Value, + line::Value, + attr::Value; + res=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ptr, annotation, fileName, line, attr] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.ptr.annotation", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_rint` + +""" +function intr_rint( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.rint", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_roundeven` + +""" +function intr_roundeven( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.roundeven", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_round` + +""" +function intr_round( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.round", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_sadd_sat` + +""" +function intr_sadd_sat( + a::Value, b::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.sadd.sat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_sadd_with_overflow` + +""" +function intr_sadd_with_overflow( + operand_0::Value, operand_1::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.sadd.with.overflow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_smax` + +""" +function intr_smax( + a::Value, b::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.smax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_smin` + +""" +function intr_smin( + a::Value, b::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.smin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_smul_with_overflow` + +""" +function intr_smul_with_overflow( + operand_0::Value, operand_1::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.smul.with.overflow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ssa_copy` + +""" +function intr_ssa_copy( + operand::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.ssa.copy", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_sshl_sat` + +""" +function intr_sshl_sat( + a::Value, b::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.sshl.sat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_ssub_sat` + +""" +function intr_ssub_sat( + a::Value, b::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.ssub.sat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_ssub_with_overflow` + +""" +function intr_ssub_with_overflow( + operand_0::Value, operand_1::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.ssub.with.overflow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_sin` + +""" +function intr_sin( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.sin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_sqrt` + +""" +function intr_sqrt( + in::Value; + res=nothing::Union{Nothing,IR.Type}, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.sqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_stackrestore` + +""" +function intr_stackrestore(ptr::Value; location=Location()) + _results = IR.Type[] + _operands = Value[ptr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.stackrestore", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_stacksave` + +""" +function intr_stacksave(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.stacksave", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_experimental_stepvector` + +""" +function intr_experimental_stepvector(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.experimental.stepvector", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_threadlocal_address` + +""" +function intr_threadlocal_address(global_::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[global_,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.threadlocal.address", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_trap` + +""" +function intr_trap(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.trap", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_uadd_sat` + +""" +function intr_uadd_sat( + a::Value, b::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.uadd.sat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_uadd_with_overflow` + +""" +function intr_uadd_with_overflow( + operand_0::Value, operand_1::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.uadd.with.overflow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ubsantrap` + +""" +function intr_ubsantrap(; failureKind, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("failureKind", failureKind),] + + return IR.create_operation( + "llvm.intr.ubsantrap", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_umax` + +""" +function intr_umax( + a::Value, b::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.umax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_umin` + +""" +function intr_umin( + a::Value, b::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.umin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_umul_with_overflow` + +""" +function intr_umul_with_overflow( + operand_0::Value, operand_1::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.umul.with.overflow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_ushl_sat` + +""" +function intr_ushl_sat( + a::Value, b::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.ushl.sat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_usub_sat` + +""" +function intr_usub_sat( + a::Value, b::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.usub.sat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_usub_with_overflow` + +""" +function intr_usub_with_overflow( + operand_0::Value, operand_1::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.usub.with.overflow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_ashr` + +""" +function intr_vp_ashr( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.ashr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_add` + +""" +function intr_vp_add( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_and` + +""" +function intr_vp_and( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.and", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_fadd` + +""" +function intr_vp_fadd( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.fadd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_fdiv` + +""" +function intr_vp_fdiv( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.fdiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_fmuladd` + +""" +function intr_vp_fmuladd( + op1::Value, + op2::Value, + op3::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[op1, op2, op3, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.fmuladd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_fmul` + +""" +function intr_vp_fmul( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.fmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_fneg` + +""" +function intr_vp_fneg(op::Value, mask::Value, evl::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[op, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.fneg", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_fpext` + +""" +function intr_vp_fpext( + src::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[src, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.fpext", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_fptosi` + +""" +function intr_vp_fptosi( + src::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[src, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.fptosi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_fptoui` + +""" +function intr_vp_fptoui( + src::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[src, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.fptoui", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_fptrunc` + +""" +function intr_vp_fptrunc( + src::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[src, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.fptrunc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_frem` + +""" +function intr_vp_frem( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.frem", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_fsub` + +""" +function intr_vp_fsub( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.fsub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_fma` + +""" +function intr_vp_fma( + op1::Value, + op2::Value, + op3::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[op1, op2, op3, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.fma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_inttoptr` + +""" +function intr_vp_inttoptr( + src::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[src, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.inttoptr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_lshr` + +""" +function intr_vp_lshr( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.lshr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_load` + +""" +function intr_vp_load( + ptr::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[ptr, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_merge` + +""" +function intr_vp_merge( + cond::Value, + true_val::Value, + false_val::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[cond, true_val, false_val, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.merge", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_mul` + +""" +function intr_vp_mul( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_or` + +""" +function intr_vp_or( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.or", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_ptrtoint` + +""" +function intr_vp_ptrtoint( + src::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[src, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.ptrtoint", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_add` + +""" +function intr_vp_reduce_add( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_and` + +""" +function intr_vp_reduce_and( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.and", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_fadd` + +""" +function intr_vp_reduce_fadd( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.fadd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_fmax` + +""" +function intr_vp_reduce_fmax( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.fmax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_fmin` + +""" +function intr_vp_reduce_fmin( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.fmin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_fmul` + +""" +function intr_vp_reduce_fmul( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.fmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_mul` + +""" +function intr_vp_reduce_mul( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_or` + +""" +function intr_vp_reduce_or( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.or", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_smax` + +""" +function intr_vp_reduce_smax( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.smax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_smin` + +""" +function intr_vp_reduce_smin( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.smin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_umax` + +""" +function intr_vp_reduce_umax( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.umax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_umin` + +""" +function intr_vp_reduce_umin( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.umin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_reduce_xor` + +""" +function intr_vp_reduce_xor( + satrt_value::Value, + val::Value, + mask::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[satrt_value, val, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.reduce.xor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_sdiv` + +""" +function intr_vp_sdiv( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.sdiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_sext` + +""" +function intr_vp_sext( + src::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[src, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.sext", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_sitofp` + +""" +function intr_vp_sitofp( + src::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[src, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.sitofp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_srem` + +""" +function intr_vp_srem( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.srem", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_select` + +""" +function intr_vp_select( + cond::Value, + true_val::Value, + false_val::Value, + evl::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[cond, true_val, false_val, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.select", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_shl` + +""" +function intr_vp_shl( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.shl", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_store` + +""" +function intr_vp_store(val::Value, ptr::Value, mask::Value, evl::Value; location=Location()) + _results = IR.Type[] + _operands = Value[val, ptr, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_experimental_vp_strided_load` + +""" +function intr_experimental_vp_strided_load( + ptr::Value, stride::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[ptr, stride, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.experimental.vp.strided.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_experimental_vp_strided_store` + +""" +function intr_experimental_vp_strided_store( + val::Value, ptr::Value, stride::Value, mask::Value, evl::Value; location=Location() +) + _results = IR.Type[] + _operands = Value[val, ptr, stride, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.experimental.vp.strided.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_sub` + +""" +function intr_vp_sub( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.sub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_trunc` + +""" +function intr_vp_trunc( + src::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[src, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.trunc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_udiv` + +""" +function intr_vp_udiv( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.udiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_uitofp` + +""" +function intr_vp_uitofp( + src::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[src, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.uitofp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_urem` + +""" +function intr_vp_urem( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.urem", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_xor` + +""" +function intr_vp_xor( + lhs::Value, rhs::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.xor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vp_zext` + +""" +function intr_vp_zext( + src::Value, mask::Value, evl::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[src, mask, evl] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vp.zext", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vacopy` + +""" +function intr_vacopy(dest_list::Value, src_list::Value; location=Location()) + _results = IR.Type[] + _operands = Value[dest_list, src_list] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vacopy", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vaend` + +""" +function intr_vaend(arg_list::Value; location=Location()) + _results = IR.Type[] + _operands = Value[arg_list,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vaend", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vastart` + +""" +function intr_vastart(arg_list::Value; location=Location()) + _results = IR.Type[] + _operands = Value[arg_list,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vastart", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_var_annotation` + +""" +function intr_var_annotation( + val::Value, + annotation::Value, + fileName::Value, + line::Value, + attr::Value; + location=Location(), +) + _results = IR.Type[] + _operands = Value[val, annotation, fileName, line, attr] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.var.annotation", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_masked_compressstore` + +""" +function intr_masked_compressstore( + operand_0::Value, operand_1::Value, operand_2::Value; location=Location() +) + _results = IR.Type[] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.masked.compressstore", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_masked_expandload` + +""" +function intr_masked_expandload( + operand_0::Value, operand_1::Value, operand_2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[operand_0, operand_1, operand_2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.masked.expandload", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_masked_gather` + +""" +function intr_masked_gather( + ptrs::Value, + mask::Value, + pass_thru::Vector{Value}; + res::IR.Type, + alignment, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[ptrs, mask, pass_thru...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("alignment", alignment),] + + return IR.create_operation( + "llvm.intr.masked.gather", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_masked_scatter` + +""" +function intr_masked_scatter( + value::Value, ptrs::Value, mask::Value; alignment, location=Location() +) + _results = IR.Type[] + _operands = Value[value, ptrs, mask] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("alignment", alignment),] + + return IR.create_operation( + "llvm.intr.masked.scatter", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_deinterleave2` + +""" +function intr_vector_deinterleave2(vec::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[vec,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vector.deinterleave2", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_extract` + +""" +function intr_vector_extract(srcvec::Value; res::IR.Type, pos, location=Location()) + _results = IR.Type[res,] + _operands = Value[srcvec,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("pos", pos),] + + return IR.create_operation( + "llvm.intr.vector.extract", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_insert` + +""" +function intr_vector_insert( + dstvec::Value, + srcvec::Value; + res=nothing::Union{Nothing,IR.Type}, + pos, + location=Location(), +) + _results = IR.Type[] + _operands = Value[dstvec, srcvec] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("pos", pos),] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "llvm.intr.vector.insert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`intr_vector_interleave2` + +""" +function intr_vector_interleave2( + vec1::Value, vec2::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[vec1, vec2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vector.interleave2", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_add` + +""" +function intr_vector_reduce_add(in::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vector.reduce.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_and` + +""" +function intr_vector_reduce_and(in::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vector.reduce.and", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_fadd` + +""" +function intr_vector_reduce_fadd( + start_value::Value, + input::Value; + res::IR.Type, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[start_value, input] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.vector.reduce.fadd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_fmax` + +""" +function intr_vector_reduce_fmax( + in::Value; res::IR.Type, fastmathFlags=nothing, location=Location() +) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.vector.reduce.fmax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_fmaximum` + +""" +function intr_vector_reduce_fmaximum( + in::Value; res::IR.Type, fastmathFlags=nothing, location=Location() +) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.vector.reduce.fmaximum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_fmin` + +""" +function intr_vector_reduce_fmin( + in::Value; res::IR.Type, fastmathFlags=nothing, location=Location() +) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.vector.reduce.fmin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_fminimum` + +""" +function intr_vector_reduce_fminimum( + in::Value; res::IR.Type, fastmathFlags=nothing, location=Location() +) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.vector.reduce.fminimum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_fmul` + +""" +function intr_vector_reduce_fmul( + start_value::Value, + input::Value; + res::IR.Type, + fastmathFlags=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[start_value, input] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(fastmathFlags) && + push!(_attributes, namedattribute("fastmathFlags", fastmathFlags)) + + return IR.create_operation( + "llvm.intr.vector.reduce.fmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_mul` + +""" +function intr_vector_reduce_mul(in::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vector.reduce.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_or` + +""" +function intr_vector_reduce_or(in::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vector.reduce.or", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_smax` + +""" +function intr_vector_reduce_smax(in::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vector.reduce.smax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_smin` + +""" +function intr_vector_reduce_smin(in::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vector.reduce.smin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_umax` + +""" +function intr_vector_reduce_umax(in::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vector.reduce.umax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_umin` + +""" +function intr_vector_reduce_umin(in::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vector.reduce.umin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vector_reduce_xor` + +""" +function intr_vector_reduce_xor(in::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vector.reduce.xor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intr_vscale` + +""" +function intr_vscale(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "llvm.intr.vscale", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`barrier0` + +""" +function barrier0(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.barrier0", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`barrier_arrive` + +Thread that executes this op announces their arrival at the barrier with +given id and continue their execution. + +The default barrier id is 0 that is similar to `nvvm.barrier` Op. When +`barrierId` is not present, the default barrier id is used. + +[For more information, see PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-bar) +""" +function barrier_arrive( + barrierId=nothing::Union{Nothing,Value}; numberOfThreads::Value, location=Location() +) + _results = IR.Type[] + _operands = Value[numberOfThreads,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(barrierId) && push!(_operands, barrierId) + + return IR.create_operation( + "nvvm.barrier.arrive", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`barrier` + +""" +function barrier( + barrierId=nothing::Union{Nothing,Value}; + numberOfThreads=nothing::Union{Nothing,Value}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(barrierId) && push!(_operands, barrierId) + !isnothing(numberOfThreads) && push!(_operands, numberOfThreads) + push!( + _attributes, + operandsegmentsizes([ + isnothing(barrierId) ? 0 : 1, isnothing(numberOfThreads) ? 0 : 1 + ]), + ) + + return IR.create_operation( + "nvvm.barrier", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_ntid_x` + +""" +function read_ptx_sreg_ntid_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.ntid.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_ntid_y` + +""" +function read_ptx_sreg_ntid_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.ntid.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_ntid_z` + +""" +function read_ptx_sreg_ntid_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.ntid.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_ctaid_x` + +""" +function read_ptx_sreg_ctaid_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.ctaid.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_ctaid_y` + +""" +function read_ptx_sreg_ctaid_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.ctaid.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_ctaid_z` + +""" +function read_ptx_sreg_ctaid_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.ctaid.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_cluster_ctaid_x` + +""" +function read_ptx_sreg_cluster_ctaid_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.cluster.ctaid.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_cluster_ctaid_y` + +""" +function read_ptx_sreg_cluster_ctaid_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.cluster.ctaid.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_cluster_ctaid_z` + +""" +function read_ptx_sreg_cluster_ctaid_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.cluster.ctaid.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_clock64` + +""" +function read_ptx_sreg_clock64(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.clock64", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_clock` + +""" +function read_ptx_sreg_clock(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.clock", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cluster_arrive` + +The `cluster.arrive` can be used by the threads within the cluster for synchronization and +communication. The `cluster.arrive` instruction marks the warps\' arrival at the barrier +without causing the executing thread to wait for other participating threads. + +The `aligned` attribute, when provided, generates the .aligned version of the PTX instruction. + +[For more information, see PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-barrier-cluster) +""" +function cluster_arrive(; aligned=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(aligned) && push!(_attributes, namedattribute("aligned", aligned)) + + return IR.create_operation( + "nvvm.cluster.arrive", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cluster_arrive_relaxed` + +The `cluster.arrive` can be used by the threads within the cluster for synchronization and +communication. The `cluster.arrive` instruction marks the warps\' arrival at the barrier +without causing the executing thread to wait for other participating threads. + +The `aligned` attribute, when provided, generates the .aligned version of the PTX instruction. +The .relaxed qualifier on `cluster.arrive` specifies that there are no memory +ordering and visibility guarantees provided for the memory accesses performed prior to +`cluster.arrive`. + +[For more information, see PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-barrier-cluster) +""" +function cluster_arrive_relaxed(; aligned=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(aligned) && push!(_attributes, namedattribute("aligned", aligned)) + + return IR.create_operation( + "nvvm.cluster.arrive.relaxed", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_cluster_nctarank` + +""" +function read_ptx_sreg_cluster_nctarank(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.cluster.nctarank", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_cluster_nctaid_x` + +""" +function read_ptx_sreg_cluster_nctaid_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.cluster.nctaid.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_cluster_nctaid_y` + +""" +function read_ptx_sreg_cluster_nctaid_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.cluster.nctaid.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_cluster_nctaid_z` + +""" +function read_ptx_sreg_cluster_nctaid_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.cluster.nctaid.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_nclusterid_x` + +""" +function read_ptx_sreg_nclusterid_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.nclusterid.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_nclusterid_y` + +""" +function read_ptx_sreg_nclusterid_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.nclusterid.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_nclusterid_z` + +""" +function read_ptx_sreg_nclusterid_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.nclusterid.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_cluster_ctarank` + +""" +function read_ptx_sreg_cluster_ctarank(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.cluster.ctarank", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_clusterid_x` + +""" +function read_ptx_sreg_clusterid_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.clusterid.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_clusterid_y` + +""" +function read_ptx_sreg_clusterid_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.clusterid.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_clusterid_z` + +""" +function read_ptx_sreg_clusterid_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.clusterid.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cluster_wait` + +The `cluster.wait` causes the executing thread to wait for all non-exited threads +of the cluster to perform `cluster.arrive`. The `aligned` attribute, when provided, +generates the .aligned version of the PTX instruction. + +[For more information, see PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-barrier-cluster) +""" +function cluster_wait(; aligned=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(aligned) && push!(_attributes, namedattribute("aligned", aligned)) + + return IR.create_operation( + "nvvm.cluster.wait", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cp_async_bulk_commit_group` + +This Op commits all prior initiated but uncommitted cp.async.bulk +instructions into a cp.async.bulk-group. + +[For more information, see PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-commit-group) +""" +function cp_async_bulk_commit_group(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.cp.async.bulk.commit.group", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cp_async_bulk_tensor_shared_cluster_global` + +Initiates an asynchronous copy operation on the tensor data from global +memory to shared memory. + +The Op operates has two load modes: +1) Tiled Mode: It\'s the default mode. The source multi-dimensional tensor +layout is preserved at the destination. + +2) Im2col Mode: This mode is used when `im2colOffsets` operands are present. +the elements in the Bounding Box of the source tensor are rearranged into +columns at the destination. In this mode, the tensor has to be at least +3-dimensional. + +The `multicastMask` operand is optional. When it is present, the Op copies +data from global memory to shared memory of multiple CTAs in the cluster. +Operand `multicastMask` specifies the destination CTAs in the cluster such +that each bit position in the 16-bit `multicastMask` operand corresponds to +the `nvvm.read.ptx.sreg.ctaid` of the destination CTA. + +The `l2CacheHint` operand is optional, and it is used to specify cache +eviction policy that may be used during the memory access. + +[For more information, see PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor) +""" +function cp_async_bulk_tensor_shared_cluster_global( + dstMem::Value, + tmaDescriptor::Value, + coordinates::Vector{Value}, + mbar::Value, + im2colOffsets::Vector{Value}, + multicastMask=nothing::Union{Nothing,Value}; + l2CacheHint=nothing::Union{Nothing,Value}, + predicate=nothing::Union{Nothing,Value}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[dstMem, tmaDescriptor, coordinates..., mbar, im2colOffsets...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(multicastMask) && push!(_operands, multicastMask) + !isnothing(l2CacheHint) && push!(_operands, l2CacheHint) + !isnothing(predicate) && push!(_operands, predicate) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + length(coordinates), + 1, + length(im2colOffsets), + isnothing(multicastMask) ? 0 : 1, + isnothing(l2CacheHint) ? 0 : 1, + isnothing(predicate) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "nvvm.cp.async.bulk.tensor.shared.cluster.global", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cp_async_bulk_tensor_global_shared_cta` + +""" +function cp_async_bulk_tensor_global_shared_cta( + tmaDescriptor::Value, + srcMem::Value, + coordinates::Vector{Value}, + predicate=nothing::Union{Nothing,Value}; + location=Location(), +) + _results = IR.Type[] + _operands = Value[tmaDescriptor, srcMem, coordinates...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(predicate) && push!(_operands, predicate) + push!( + _attributes, + operandsegmentsizes([1, 1, length(coordinates), isnothing(predicate) ? 0 : 1]), + ) + + return IR.create_operation( + "nvvm.cp.async.bulk.tensor.global.shared.cta", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cp_async_bulk_wait_group` + +Op waits for completion of the most recent bulk async-groups. + +The `\$group` operand tells waiting has to be done until for \$group or fewer +of the most recent bulk async-groups. If `\$group` is 0, the op wait until +all the most recent bulk async-groups have completed. + +The `\$read` indicates that the waiting has to be done until all the bulk +async operations in the specified bulk async-group have completed reading +from their source locations. + +[For more information, see PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-wait-group) +""" +function cp_async_bulk_wait_group(; group, read=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("group", group),] + !isnothing(read) && push!(_attributes, namedattribute("read", read)) + + return IR.create_operation( + "nvvm.cp.async.bulk.wait_group", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cp_async_commit_group` + +""" +function cp_async_commit_group(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.cp.async.commit.group", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cp_async_mbarrier_arrive` + +The `cp.async.mbarrier.arrive` Op makes the mbarrier object track +all prior cp.async operations initiated by the executing thread. +The `addr` operand specifies the address of the mbarrier object +in generic address space. The `noinc` attr impacts how the +mbarrier\'s state is updated. +[For more information, refer PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-cp-async-mbarrier-arrive) +""" +function cp_async_mbarrier_arrive(addr::Value; noinc=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[addr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(noinc) && push!(_attributes, namedattribute("noinc", noinc)) + + return IR.create_operation( + "nvvm.cp.async.mbarrier.arrive", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cp_async_mbarrier_arrive_shared` + +The `cp.async.mbarrier.arrive.shared` Op makes the mbarrier object +track all prior cp.async operations initiated by the executing thread. +The `addr` operand specifies the address of the mbarrier object in +shared memory. The `noinc` attr impacts how the mbarrier\'s state +is updated. [For more information, refer PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-cp-async-mbarrier-arrive) +""" +function cp_async_mbarrier_arrive_shared(addr::Value; noinc=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[addr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(noinc) && push!(_attributes, namedattribute("noinc", noinc)) + + return IR.create_operation( + "nvvm.cp.async.mbarrier.arrive.shared", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cp_async_shared_global` + +""" +function cp_async_shared_global( + dst::Value, + src::Value, + cpSize=nothing::Union{Nothing,Value}; + size, + modifier, + location=Location(), +) + _results = IR.Type[] + _operands = Value[dst, src] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("size", size), namedattribute("modifier", modifier) + ] + !isnothing(cpSize) && push!(_operands, cpSize) + + return IR.create_operation( + "nvvm.cp.async.shared.global", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cp_async_wait_group` + +""" +function cp_async_wait_group(; n, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("n", n),] + + return IR.create_operation( + "nvvm.cp.async.wait.group", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`elect_sync` + +""" +function elect_sync(; pred::IR.Type, location=Location()) + _results = IR.Type[pred,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.elect.sync", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fence_mbarrier_init` + +Fence operation that applies on the prior nvvm.mbarrier.init +[For more information, see PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-membar) +""" +function fence_mbarrier_init(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.fence.mbarrier.init", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fence_proxy` + +Fence operation with proxy to establish an ordering between memory accesses +that may happen through different proxies. +[For more information, see PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-membar) +""" +function fence_proxy(; kind, space=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("kind", kind),] + !isnothing(space) && push!(_attributes, namedattribute("space", space)) + + return IR.create_operation( + "nvvm.fence.proxy", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fence_sc_cluster` + +""" +function fence_sc_cluster(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.fence.sc.cluster", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_nctaid_x` + +""" +function read_ptx_sreg_nctaid_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.nctaid.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_nctaid_y` + +""" +function read_ptx_sreg_nctaid_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.nctaid.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_nctaid_z` + +""" +function read_ptx_sreg_nctaid_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.nctaid.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_laneid` + +""" +function read_ptx_sreg_laneid(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.laneid", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ldmatrix` + +""" +function ldmatrix(ptr::Value; res::IR.Type, num, layout, location=Location()) + _results = IR.Type[res,] + _operands = Value[ptr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("num", num), namedattribute("layout", layout) + ] + + return IR.create_operation( + "nvvm.ldmatrix", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_arrive_expect_tx` + +""" +function mbarrier_arrive_expect_tx( + addr::Value, + txcount::Value, + predicate=nothing::Union{Nothing,Value}; + location=Location(), +) + _results = IR.Type[] + _operands = Value[addr, txcount] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(predicate) && push!(_operands, predicate) + + return IR.create_operation( + "nvvm.mbarrier.arrive.expect_tx", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_arrive_expect_tx_shared` + +""" +function mbarrier_arrive_expect_tx_shared( + addr::Value, + txcount::Value, + predicate=nothing::Union{Nothing,Value}; + location=Location(), +) + _results = IR.Type[] + _operands = Value[addr, txcount] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(predicate) && push!(_operands, predicate) + + return IR.create_operation( + "nvvm.mbarrier.arrive.expect_tx.shared", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_arrive_nocomplete` + +""" +function mbarrier_arrive_nocomplete( + addr::Value, count::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[addr, count] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.mbarrier.arrive.nocomplete", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_arrive_nocomplete_shared` + +""" +function mbarrier_arrive_nocomplete_shared( + addr::Value, count::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[addr, count] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.mbarrier.arrive.nocomplete.shared", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_arrive` + +""" +function mbarrier_arrive(addr::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[addr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.mbarrier.arrive", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_arrive_shared` + +""" +function mbarrier_arrive_shared(addr::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[addr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.mbarrier.arrive.shared", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_init` + +""" +function mbarrier_init( + addr::Value, count::Value, predicate=nothing::Union{Nothing,Value}; location=Location() +) + _results = IR.Type[] + _operands = Value[addr, count] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(predicate) && push!(_operands, predicate) + + return IR.create_operation( + "nvvm.mbarrier.init", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_init_shared` + +""" +function mbarrier_init_shared( + addr::Value, count::Value, predicate=nothing::Union{Nothing,Value}; location=Location() +) + _results = IR.Type[] + _operands = Value[addr, count] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(predicate) && push!(_operands, predicate) + + return IR.create_operation( + "nvvm.mbarrier.init.shared", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_inval` + +""" +function mbarrier_inval(addr::Value; location=Location()) + _results = IR.Type[] + _operands = Value[addr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.mbarrier.inval", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_inval_shared` + +""" +function mbarrier_inval_shared(addr::Value; location=Location()) + _results = IR.Type[] + _operands = Value[addr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.mbarrier.inval.shared", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_test_wait` + +""" +function mbarrier_test_wait(addr::Value, state::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[addr, state] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.mbarrier.test.wait", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_test_wait_shared` + +""" +function mbarrier_test_wait_shared( + addr::Value, state::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[addr, state] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.mbarrier.test.wait.shared", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_try_wait_parity` + +""" +function mbarrier_try_wait_parity( + addr::Value, phase::Value, ticks::Value; location=Location() +) + _results = IR.Type[] + _operands = Value[addr, phase, ticks] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.mbarrier.try_wait.parity", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_try_wait_parity_shared` + +""" +function mbarrier_try_wait_parity_shared( + addr::Value, phase::Value, ticks::Value; location=Location() +) + _results = IR.Type[] + _operands = Value[addr, phase, ticks] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.mbarrier.try_wait.parity.shared", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mma_sync` + +The `nvvm.mma.sync` operation collectively performs the operation +`D = matmul(A, B) + C` using all threads in a warp. + +All the threads in the warp must execute the same `mma.sync` operation. + +For each possible multiplicand PTX data type, there are one or more possible +instruction shapes given as \"mMnNkK\". The below table describes the posssibilities +as well as the types required for the operands. Note that the data type for +C (the accumulator) and D (the result) can vary independently when there are +multiple possibilities in the \"C/D Type\" column. + +When an optional attribute cannot be immediately inferred from the types of +the operands and the result during parsing or validation, an error will be +raised. + +`b1Op` is only relevant when the binary (b1) type is given to +`multiplicandDataType`. It specifies how the multiply-and-acumulate is +performed and is either `xor_popc` or `and_poc`. The default is `xor_popc`. + +`intOverflowBehavior` is only relevant when the `multiplicandType` attribute +is one of `u8, s8, u4, s4`, this attribute describes how overflow is handled +in the accumulator. When the attribute is `satfinite`, the accumulator values +are clamped in the int32 range on overflow. This is the default behavior. +Alternatively, accumulator behavior `wrapped` can also be specified, in +which case overflow wraps from one end of the range to the other. + +`layoutA` and `layoutB` are required and should generally be set to +`#nvvm.mma_layout` and `#nvvm.mma_layout` respectively, but other +combinations are possible for certain layouts according to the table below. + +``` +| A/B Type | Shape | ALayout | BLayout | A Type | B Type | C/D Type | +|----------|-----------|---------|---------|----------|----------|-------------------| +| f64 | .m8n8k4 | row | col | 1x f64 | 1x f64 | 2x f64 | +| f16 | .m8n8k4 | row/col | row/col | 2x f16x2 | 2x f16x2 | 4x f16x2 or 8xf32 | +| | .m16n8k8 | row | col | 2x f16x2 | 1x f16x2 | 2x f16x2 or 4 f32 | +| | .m16n8k16 | row | col | 4x f16x2 | 2x f16x2 | 2x f16x2 or 4 f32 | +| bf16 | .m16n8k8 | row | col | 2x f16x2 | 1x f16x2 | 2x f16x2 or 4 f32 | +| | .m16n8k16 | row | col | 4x f16x2 | 2x f16x2 | 2x f16x2 or 4 f32 | +| tf32 | .m16n8k4 | row | col | 2x i32 | 1x i32 | 4x f32 | +| | .m16n8k8 | row | col | 4x i32 | 2x i32 | 2x f16x2 or 4 f32 | +| u8/s8 | .m8n8k16 | row | col | 1x i32 | 1x i32 | 2x i32 | +| | .m16n8k16 | row | col | 2x i32 | 1x i32 | 4x i32 | +| | .m16n8k32 | row | col | 4x i32 | 2x i32 | 4x i32 | +| u4/s4 | .m8n8k32 | row | col | 1x i32 | 1x i32 | 2x i32 | +| | m16n8k32 | row | col | 2x i32 | 1x i32 | 4x i32 | +| | m16n8k64 | row | col | 4x i32 | 2x i32 | 4x i32 | +| b1 | m8n8k128 | row | col | 1x i32 | 1x i32 | 2x i32 | +| | m16n8k128 | row | col | 2x i32 | 1x i32 | 4x i32 | +``` + + +# Example +```mlir + +%128 = nvvm.mma.sync A[%120, %121, %122, %123] + B[%124, %125] + C[%126, %127] + {layoutA = #nvvm.mma_layout, + layoutB = #nvvm.mma_layout, + shape = {k = 16 : i32, m = 16 : i32, n = 8 : i32}} + : (vector<2xf16>, vector<2xf16>, vector<2xf16>) + -> !llvm.struct<(vector<2xf16>, vector<2xf16>)> +``` +""" +function mma_sync( + operandA::Vector{Value}, + operandB::Vector{Value}, + operandC::Vector{Value}; + res::IR.Type, + shape, + b1Op=nothing, + intOverflowBehavior=nothing, + layoutA, + layoutB, + multiplicandAPtxType=nothing, + multiplicandBPtxType=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[operandA..., operandB..., operandC...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("shape", shape), + namedattribute("layoutA", layoutA), + namedattribute("layoutB", layoutB), + ] + push!( + _attributes, + operandsegmentsizes([length(operandA), length(operandB), length(operandC)]), + ) + !isnothing(b1Op) && push!(_attributes, namedattribute("b1Op", b1Op)) + !isnothing(intOverflowBehavior) && + push!(_attributes, namedattribute("intOverflowBehavior", intOverflowBehavior)) + !isnothing(multiplicandAPtxType) && + push!(_attributes, namedattribute("multiplicandAPtxType", multiplicandAPtxType)) + !isnothing(multiplicandBPtxType) && + push!(_attributes, namedattribute("multiplicandBPtxType", multiplicandBPtxType)) + + return IR.create_operation( + "nvvm.mma.sync", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`prefetch_tensormap` + +""" +function prefetch_tensormap( + tmaDescriptor::Value, predicate=nothing::Union{Nothing,Value}; location=Location() +) + _results = IR.Type[] + _operands = Value[tmaDescriptor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(predicate) && push!(_operands, predicate) + + return IR.create_operation( + "nvvm.prefetch.tensormap", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`rcp_approx_ftz_f` + +""" +function rcp_approx_ftz_f(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.rcp.approx.ftz.f", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`redux_sync` + +""" +function redux_sync( + val::Value, mask_and_clamp::Value; res::IR.Type, kind, location=Location() +) + _results = IR.Type[res,] + _operands = Value[val, mask_and_clamp] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("kind", kind),] + + return IR.create_operation( + "nvvm.redux.sync", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`setmaxregister` + +""" +function setmaxregister(; regCount, action, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("regCount", regCount), namedattribute("action", action) + ] + + return IR.create_operation( + "nvvm.setmaxregister", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`shfl_sync` + +The `shfl.sync` Op implements data shuffle within threads of a warp. +The `thread_mask` denotes the threads participating in the Op where +the bit position corresponds to a particular thread’s laneid. +The `offset` specifies a source lane or source lane offset +(depending on `kind`). The `val` is the input value to be copied from +the source. The `mask_and_clamp` contains two packed values specifying +a mask for logically splitting warps into sub-segments and an upper bound +for clamping the source lane index. +[For more information, refer PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-shfl-sync) +""" +function shfl_sync( + thread_mask::Value, + val::Value, + offset::Value, + mask_and_clamp::Value; + res::IR.Type, + kind, + return_value_and_is_valid=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[thread_mask, val, offset, mask_and_clamp] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("kind", kind),] + !isnothing(return_value_and_is_valid) && push!( + _attributes, + namedattribute("return_value_and_is_valid", return_value_and_is_valid), + ) + + return IR.create_operation( + "nvvm.shfl.sync", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`stmatrix` + +Collectively store one or more matrices across all threads in a warp to the +location indicated by the address operand \$ptr in shared memory. +[For more information, see PTX ISA] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#warp-level-matrix-store-instruction-stmatrix) +""" +function stmatrix(ptr::Value, sources::Vector{Value}; layout, location=Location()) + _results = IR.Type[] + _operands = Value[ptr, sources...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("layout", layout),] + + return IR.create_operation( + "nvvm.stmatrix", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bar_warp_sync` + +""" +function bar_warp_sync(mask::Value; location=Location()) + _results = IR.Type[] + _operands = Value[mask,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.bar.warp.sync", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_tid_x` + +""" +function read_ptx_sreg_tid_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.tid.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_tid_y` + +""" +function read_ptx_sreg_tid_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.tid.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_tid_z` + +""" +function read_ptx_sreg_tid_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.tid.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`vote_ballot_sync` + +""" +function vote_ballot_sync(mask::Value, pred::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[mask, pred] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.vote.ballot.sync", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wmma_load` + +""" +function wmma_load( + ptr::Value, + stride::Value; + res::IR.Type, + m, + n, + k, + layout, + eltype, + frag, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[ptr, stride] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("m", m), + namedattribute("n", n), + namedattribute("k", k), + namedattribute("layout", layout), + namedattribute("eltype", eltype), + namedattribute("frag", frag), + ] + + return IR.create_operation( + "nvvm.wmma.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wmma_mma` + +""" +function wmma_mma( + args::Vector{Value}; + res::IR.Type, + m, + n, + k, + layoutA, + layoutB, + eltypeA, + eltypeB, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("m", m), + namedattribute("n", n), + namedattribute("k", k), + namedattribute("layoutA", layoutA), + namedattribute("layoutB", layoutB), + namedattribute("eltypeA", eltypeA), + namedattribute("eltypeB", eltypeB), + ] + + return IR.create_operation( + "nvvm.wmma.mma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wmma_store` + +""" +function wmma_store( + ptr::Value, + args::Vector{Value}, + stride::Value; + m, + n, + k, + layout, + eltype, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ptr, args..., stride] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("m", m), + namedattribute("n", n), + namedattribute("k", k), + namedattribute("layout", layout), + namedattribute("eltype", eltype), + ] + + return IR.create_operation( + "nvvm.wmma.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`read_ptx_sreg_warpsize` + +""" +function read_ptx_sreg_warpsize(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.read.ptx.sreg.warpsize", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wgmma_fence_aligned` + +Enforce an ordering of register accesses between warpgroup level matrix +multiplication and other operations. + +[For more information, see PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#asynchronous-warpgroup-level-matrix-instructions-wgmma-fence) +""" +function wgmma_fence_aligned(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.wgmma.fence.aligned", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wgmma_commit_group_sync_aligned` + +Commits all prior uncommitted warpgroup level matrix multiplication operations. + +[For more information, see PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#asynchronous-warpgroup-level-matrix-instructions-wgmma-commit-group) +""" +function wgmma_commit_group_sync_aligned(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvvm.wgmma.commit.group.sync.aligned", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wgmma_mma_async` + +The warpgroup (128 threads) level matrix multiply and accumulate operation +has either of the following forms, where matrix D is called accumulator: + D = A * B + D + D = A * B, where the input from accumulator D is disabled. + +Supported shapes: +``` +|--------------|--------------|------------|--------------|---------------| +| | | | |f16+=e4m3*e4m3 | +| | | | |f16+=e5m2*e5m2 | +|f32+=tf32*tf32|f16+=f16 *f16 | s32+=s8*s8 |s32 += b1 * b1|f16+=e5m2*e4m3 | +| |f32+=f16 *f16 | s32+=u8*u8 | |f16+=e4m3*e5m2 | +| |f32+=bf16*bf16| s32+=u8*u8 | |f16+=e4m3*e5m2 | +| |f32+=bf16*bf16| s32+=s8*u8 | |f32+=e4m3*e4m3 | +| | | s32+=u8*s8 | |f32+=e5m2*e5m2 | +| | | | |f32+=e4m3*e5m2 | +| | | | |f32+=e4m3*e5m2 | +|--------------|--------------|------------|--------------|---------------| +| .m64n8k8 | .m64n8k16 | .m64n8k32 | .m64n8k256 | .m64n8k32 | +| .m64n16k8 | .m64n16k16 | .m64n16k32 | .m64n16k256 | .m64n16k32 | +| .m64n24k8 | .m64n24k16 | .m64n24k32 | .m64n24k256 | .m64n24k32 | +| .m64n32k8 | .m64n32k16 | .m64n32k32 | .m64n32k256 | .m64n32k32 | +| .m64n40k8 | .m64n40k16 | .m64n48k32 | .m64n48k256 | .m64n40k32 | +| .m64n48k8 | .m64n48k16 | .m64n64k32 | .m64n64k256 | .m64n48k32 | +| .m64n56k8 | .m64n56k16 | .m64n80k32 | .m64n80k256 | .m64n56k32 | +| .m64n64k8 | .m64n64k16 | .m64n96k32 | .m64n96k256 | .m64n64k32 | +| .m64n72k8 | .m64n72k16 | .m64n112k32| .m64n112k256 | .m64n72k32 | +| .m64n80k8 | .m64n80k16 | .m64n128k32| .m64n128k256 | .m64n80k32 | +| .m64n88k8 | .m64n88k16 | .m64n144k32| .m64n144k256 | .m64n88k32 | +| .m64n96k8 | .m64n96k16 | .m64n160k32| .m64n160k256 | .m64n96k32 | +| .m64n104k8 | .m64n104k16 | .m64n176k32| .m64n176k256 | .m64n104k32 | +| .m64n112k8 | .m64n112k16 | .m64n192k32| .m64n192k256 | .m64n112k32 | +| .m64n120k8 | .m64n120k16 | .m64n208k32| .m64n208k256 | .m64n120k32 | +| .m64n128k8 | .m64n128k16 | .m64n224k32| .m64n224k256 | .m64n128k32 | +| .m64n136k8 | .m64n136k16 | .m64n240k32| .m64n240k256 | .m64n136k32 | +| .m64n144k8 | .m64n144k16 | .m64n256k32| .m64n256k256 | .m64n144k32 | +| .m64n152k8 | .m64n152k16 | | | .m64n152k32 | +| .m64n160k8 | .m64n160k16 | | | .m64n160k32 | +| .m64n168k8 | .m64n168k16 | | | .m64n168k32 | +| .m64n176k8 | .m64n176k16 | | | .m64n176k32 | +| .m64n184k8 | .m64n184k16 | | | .m64n184k32 | +| .m64n192k8 | .m64n192k16 | | | .m64n192k32 | +| .m64n200k8 | .m64n200k16 | | | .m64n200k32 | +| .m64n208k8 | .m64n208k16 | | | .m64n208k32 | +| .m64n216k8 | .m64n216k16 | | | .m64n216k32 | +| .m64n224k8 | .m64n224k16 | | | .m64n224k32 | +| .m64n232k8 | .m64n232k16 | | | .m64n232k32 | +| .m64n240k8 | .m64n240k16 | | | .m64n240k32 | +| .m64n248k8 | .m64n248k16 | | | .m64n248k32 | +| .m64n256k8 | .m64n256k16 | | | .m64n256k32 | +|--------------|--------------|------------|--------------|---------------| +``` + + +[For more information, see PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#asynchronous-warpgroup-level-matrix-instructions) +""" +function wgmma_mma_async( + inouts::Value, + descriptorA::Value, + descriptorB::Value; + results::IR.Type, + shape, + typeA, + typeB, + typeD, + scaleD, + scaleA, + scaleB, + layoutA, + layoutB, + satfinite=nothing, + location=Location(), +) + _results = IR.Type[results,] + _operands = Value[inouts, descriptorA, descriptorB] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("shape", shape), + namedattribute("typeA", typeA), + namedattribute("typeB", typeB), + namedattribute("typeD", typeD), + namedattribute("scaleD", scaleD), + namedattribute("scaleA", scaleA), + namedattribute("scaleB", scaleB), + namedattribute("layoutA", layoutA), + namedattribute("layoutB", layoutB), + ] + !isnothing(satfinite) && push!(_attributes, namedattribute("satfinite", satfinite)) + + return IR.create_operation( + "nvvm.wgmma.mma_async", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wgmma_wait_group_sync_aligned` + +Signal the completion of a preceding warpgroup operation. + +[For more information, see PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#asynchronous-warpgroup-level-matrix-instructions-wgmma-wait-group) +""" +function wgmma_wait_group_sync_aligned(; group, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("group", group),] + + return IR.create_operation( + "nvvm.wgmma.wait.group.sync.aligned", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`ballot` + +Ballot provides a bit mask containing the 1-bit predicate value from each lane. +The nth bit of the result contains the 1 bit contributed by the nth warp lane. +""" +function ballot(pred::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[pred,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.ballot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`barrier` + +""" +function barrier(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.barrier", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`workgroup_dim_x` + +""" +function workgroup_dim_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.workgroup.dim.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`workgroup_dim_y` + +""" +function workgroup_dim_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.workgroup.dim.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`workgroup_dim_z` + +""" +function workgroup_dim_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.workgroup.dim.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`workgroup_id_x` + +""" +function workgroup_id_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.workgroup.id.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`workgroup_id_y` + +""" +function workgroup_id_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.workgroup.id.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`workgroup_id_z` + +""" +function workgroup_id_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.workgroup.id.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cvt_f32_bf8` + +Convert 8-bit bf8 value from the `byteSel`th bit of `srcA` to fp32. +""" +function cvt_f32_bf8(srcA::Value, byteSel::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[srcA, byteSel] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.cvt.f32.bf8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cvt_f32_fp8` + +Convert 8-bit fp8 value from the `byteSel`th bit of `srcA` to fp32. +""" +function cvt_f32_fp8(srcA::Value, byteSel::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[srcA, byteSel] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.cvt.f32.fp8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cvt_pk_bf8_f32` + +Convert `srcA` and `srcB` to bf8 and store into the low/high word of +`old`, preserving the other word. +""" +function cvt_pk_bf8_f32( + srcA::Value, srcB::Value, old::Value, wordSel::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[srcA, srcB, old, wordSel] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.cvt.pk.bf8.f32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cvt_pk_fp8_f32` + +Convert `srcA` and `srcB` to fp8 and store into the low/high word of +`old`, preserving the other word. +""" +function cvt_pk_fp8_f32( + srcA::Value, srcB::Value, old::Value, wordSel::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[srcA, srcB, old, wordSel] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.cvt.pk.fp8.f32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cvt_sr_bf8_f32` + +Convert `srcA` to bf8, adding the rounding factor from `srcB`, +and store into the `byteSel`th byte of `old`, preserving the others. +""" +function cvt_sr_bf8_f32( + srcA::Value, srcB::Value, old::Value, byteSel::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[srcA, srcB, old, byteSel] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.cvt.sr.bf8.f32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cvt_sr_fp8_f32` + +Convert `srcA` to fp8, adding the rounding factor from `srcB`, +and store into the `byteSel`th byte of `old`, preserving the others. +""" +function cvt_sr_fp8_f32( + srcA::Value, srcB::Value, old::Value, byteSel::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[srcA, srcB, old, byteSel] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.cvt.sr.fp8.f32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ds_bpermute` + +""" +function ds_bpermute(index::Value, src::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[index, src] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.ds_bpermute", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ds_swizzle` + +""" +function ds_swizzle(src::Value, offset::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[src, offset] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.ds_swizzle", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`grid_dim_x` + +""" +function grid_dim_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.grid.dim.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`grid_dim_y` + +""" +function grid_dim_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.grid.dim.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`grid_dim_z` + +""" +function grid_dim_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.grid.dim.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`make_buffer_rsrc` + +""" +function make_buffer_rsrc( + base::Value, + stride::Value, + numRecords::Value, + flags::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[base, stride, numRecords, flags] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.make.buffer.rsrc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbcnt_hi` + +""" +function mbcnt_hi(in0::Value, in1::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[in0, in1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mbcnt.hi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbcnt_lo` + +""" +function mbcnt_lo(in0::Value, in1::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[in0, in1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mbcnt.lo", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_atomic_cmpswap` + +""" +function raw_buffer_atomic_cmpswap( + src::Value, + cmp::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[src, cmp, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.raw.buffer.atomic.cmpswap", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_atomic_fadd` + +""" +function raw_buffer_atomic_fadd( + vdata::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + location=Location(), +) + _results = IR.Type[] + _operands = Value[vdata, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.raw.buffer.atomic.fadd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_atomic_fmax` + +""" +function raw_buffer_atomic_fmax( + vdata::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + location=Location(), +) + _results = IR.Type[] + _operands = Value[vdata, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.raw.buffer.atomic.fmax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_atomic_smax` + +""" +function raw_buffer_atomic_smax( + vdata::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + location=Location(), +) + _results = IR.Type[] + _operands = Value[vdata, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.raw.buffer.atomic.smax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_atomic_umin` + +""" +function raw_buffer_atomic_umin( + vdata::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + location=Location(), +) + _results = IR.Type[] + _operands = Value[vdata, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.raw.buffer.atomic.umin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_load` + +""" +function raw_buffer_load( + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + res::IR.Type, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.raw.buffer.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_buffer_store` + +""" +function raw_buffer_store( + vdata::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + location=Location(), +) + _results = IR.Type[] + _operands = Value[vdata, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.raw.buffer.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_ptr_buffer_atomic_cmpswap` + +""" +function raw_ptr_buffer_atomic_cmpswap( + src::Value, + cmp::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + res::IR.Type, + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[src, cmp, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "rocdl.raw.ptr.buffer.atomic.cmpswap", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_ptr_buffer_atomic_fadd` + +""" +function raw_ptr_buffer_atomic_fadd( + vdata::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vdata, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "rocdl.raw.ptr.buffer.atomic.fadd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_ptr_buffer_atomic_fmax` + +""" +function raw_ptr_buffer_atomic_fmax( + vdata::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vdata, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "rocdl.raw.ptr.buffer.atomic.fmax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_ptr_buffer_atomic_smax` + +""" +function raw_ptr_buffer_atomic_smax( + vdata::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vdata, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "rocdl.raw.ptr.buffer.atomic.smax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_ptr_buffer_atomic_umin` + +""" +function raw_ptr_buffer_atomic_umin( + vdata::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vdata, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "rocdl.raw.ptr.buffer.atomic.umin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_ptr_buffer_load` + +""" +function raw_ptr_buffer_load( + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + res::IR.Type, + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "rocdl.raw.ptr.buffer.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`raw_ptr_buffer_store` + +""" +function raw_ptr_buffer_store( + vdata::Value, + rsrc::Value, + offset::Value, + soffset::Value, + aux::Value; + alias_scopes=nothing, + noalias_scopes=nothing, + tbaa=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vdata, rsrc, offset, soffset, aux] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(alias_scopes) && + push!(_attributes, namedattribute("alias_scopes", alias_scopes)) + !isnothing(noalias_scopes) && + push!(_attributes, namedattribute("noalias_scopes", noalias_scopes)) + !isnothing(tbaa) && push!(_attributes, namedattribute("tbaa", tbaa)) + + return IR.create_operation( + "rocdl.raw.ptr.buffer.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`s_barrier` + +""" +function s_barrier(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.s.barrier", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sched_barrier` + +""" +function sched_barrier(; mask, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mask", mask),] + + return IR.create_operation( + "rocdl.sched.barrier", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`s_setprio` + +""" +function s_setprio(; priority, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("priority", priority),] + + return IR.create_operation( + "rocdl.s.setprio", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`workitem_id_x` + +""" +function workitem_id_x(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.workitem.id.x", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`workitem_id_y` + +""" +function workitem_id_y(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.workitem.id.y", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`workitem_id_z` + +""" +function workitem_id_z(; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.workitem.id.z", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`waitcnt` + +""" +function waitcnt(; bitfield, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("bitfield", bitfield),] + + return IR.create_operation( + "rocdl.waitcnt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_4x4x1f32` + +""" +function mfma_f32_4x4x1f32(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.4x4x1f32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_4x4x2bf16` + +""" +function mfma_f32_4x4x2bf16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.4x4x2bf16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_4x4x4bf16_1k` + +""" +function mfma_f32_4x4x4bf16_1k(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.4x4x4bf16.1k", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_4x4x4f16` + +""" +function mfma_f32_4x4x4f16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.4x4x4f16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x1f32` + +""" +function mfma_f32_16x16x1f32(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x1f32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x2bf16` + +""" +function mfma_f32_16x16x2bf16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x2bf16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x4bf16_1k` + +""" +function mfma_f32_16x16x4bf16_1k(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x4bf16.1k", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x4f16` + +""" +function mfma_f32_16x16x4f16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x4f16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x4f32` + +""" +function mfma_f32_16x16x4f32(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x4f32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x8_xf32` + +""" +function mfma_f32_16x16x8_xf32(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x8.xf32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x8bf16` + +""" +function mfma_f32_16x16x8bf16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x8bf16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x16bf16_1k` + +""" +function mfma_f32_16x16x16bf16_1k(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x16bf16.1k", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x16f16` + +""" +function mfma_f32_16x16x16f16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x16f16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x32_bf8_bf8` + +""" +function mfma_f32_16x16x32_bf8_bf8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x32.bf8.bf8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x32_bf8_fp8` + +""" +function mfma_f32_16x16x32_bf8_fp8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x32.bf8.fp8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x32_fp8_bf8` + +""" +function mfma_f32_16x16x32_fp8_bf8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x32.fp8.bf8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_16x16x32_fp8_fp8` + +""" +function mfma_f32_16x16x32_fp8_fp8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.16x16x32.fp8.fp8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x1f32` + +""" +function mfma_f32_32x32x1f32(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x1f32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x2bf16` + +""" +function mfma_f32_32x32x2bf16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x2bf16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x2f32` + +""" +function mfma_f32_32x32x2f32(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x2f32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x4_xf32` + +""" +function mfma_f32_32x32x4_xf32(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x4.xf32", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x4bf16` + +""" +function mfma_f32_32x32x4bf16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x4bf16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x4bf16_1k` + +""" +function mfma_f32_32x32x4bf16_1k(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x4bf16.1k", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x4f16` + +""" +function mfma_f32_32x32x4f16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x4f16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x8bf16_1k` + +""" +function mfma_f32_32x32x8bf16_1k(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x8bf16.1k", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x8f16` + +""" +function mfma_f32_32x32x8f16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x8f16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x16_bf8_bf8` + +""" +function mfma_f32_32x32x16_bf8_bf8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x16.bf8.bf8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x16_bf8_fp8` + +""" +function mfma_f32_32x32x16_bf8_fp8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x16.bf8.fp8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x16_fp8_bf8` + +""" +function mfma_f32_32x32x16_fp8_bf8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x16.fp8.bf8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f32_32x32x16_fp8_fp8` + +""" +function mfma_f32_32x32x16_fp8_fp8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f32.32x32x16.fp8.fp8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f64_4x4x4f64` + +""" +function mfma_f64_4x4x4f64(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f64.4x4x4f64", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_f64_16x16x4f64` + +""" +function mfma_f64_16x16x4f64(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.f64.16x16x4f64", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_i32_4x4x4i8` + +""" +function mfma_i32_4x4x4i8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.i32.4x4x4i8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_i32_16x16x4i8` + +""" +function mfma_i32_16x16x4i8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.i32.16x16x4i8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_i32_16x16x16i8` + +""" +function mfma_i32_16x16x16i8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.i32.16x16x16i8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_i32_16x16x32_i8` + +""" +function mfma_i32_16x16x32_i8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.i32.16x16x32.i8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_i32_32x32x4i8` + +""" +function mfma_i32_32x32x4i8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.i32.32x32x4i8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_i32_32x32x8i8` + +""" +function mfma_i32_32x32x8i8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.i32.32x32x8i8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mfma_i32_32x32x16_i8` + +""" +function mfma_i32_32x32x16_i8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.mfma.i32.32x32x16.i8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wmma_bf16_16x16x16_bf16` + +""" +function wmma_bf16_16x16x16_bf16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.wmma.bf16.16x16x16.bf16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wmma_f16_16x16x16_f16` + +""" +function wmma_f16_16x16x16_f16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.wmma.f16.16x16x16.f16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wmma_f32_16x16x16_bf16` + +""" +function wmma_f32_16x16x16_bf16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.wmma.f32.16x16x16.bf16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wmma_f32_16x16x16_f16` + +""" +function wmma_f32_16x16x16_f16(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.wmma.f32.16x16x16.f16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wmma_i32_16x16x16_iu4` + +""" +function wmma_i32_16x16x16_iu4(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.wmma.i32.16x16x16.iu4", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wmma_i32_16x16x16_iu8` + +""" +function wmma_i32_16x16x16_iu8(args::Vector{Value}; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "rocdl.wmma.i32.16x16x16.iu8", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # llvm diff --git a/src/Dialects/19/Linalg.jl b/src/Dialects/19/Linalg.jl new file mode 100644 index 00000000..059a6a68 --- /dev/null +++ b/src/Dialects/19/Linalg.jl @@ -0,0 +1,3577 @@ +module linalg + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`index` + +The `linalg.index` operation returns the iteration index of the immediately +enclosing linalg structured operation for the iteration dimension `dim`. The +`dim` attribute specifies the position of the accessed dimension in the +indexing map domain. + +# Example + +```mlir +#map = affine_map<(i, j) -> (i, j)> +linalg.generic {indexing_maps = [#map, #map], + iterator_types = [\"parallel\", \"parallel\"]} + outs(%I, %J : memref, memref) { + ^bb0(%arg0 : index, %arg1 : index): + // Access the outer iteration dimension i + %i = linalg.index 0 : index + // Access the inner iteration dimension j + %j = linalg.index 1 : index + linalg.yield %i, %j : index, index +} +``` + +This may lower to IR resembling: + +```mlir +%0 = dim %I, %c0 : memref +%1 = dim %I, %c1 : memref +scf.for %i = %c0 to %0 step %c1 { + scf.for %j = %c0 to %1 step %c1 { + store %i, %I[%i, %j] : memref + store %j, %J[%i, %j] : memref + } +} +``` +""" +function index(; result=nothing::Union{Nothing,IR.Type}, dim, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dim", dim),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "linalg.index", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`softmax` + +linalg.softmax computes a numerically stable version of softmax. + +For a given input tensor and a specified dimension `d`, compute: + 1. the max `m` along that dimension `d` + 2. f(x) = exp(x - m) + 3. sum f(x) along dimension d to get l(x). + 4. compute the final result f(x) / l(x). + +This is an aggregate linalg operation that further reduces to a small DAG of +structured operations. + +Warning: Regarding the tiling capabilities, the implementation doesn\'t +check that the provided dimensions make sense. This is the responsability +of the transformation calling the tiling to ensure that the provided +sizes for each dimension make sense with respect to the semantic of +softmax. +""" +function softmax( + input::Value, output::Value; result::Vector{IR.Type}, dimension, location=Location() +) + _results = IR.Type[result...,] + _operands = Value[input, output] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimension", dimension),] + + return IR.create_operation( + "linalg.softmax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`winograd_filter_transform` + +Winograd Conv2D algorithm will convert linalg Conv2D operator into batched +matrix multiply. Before the matrix multiply, it will convert filter and +input into a format suitable for batched matrix multiply. After the matrix +multiply, it will convert output to the final result tensor. + +The algorithm F(m x m, r x r) is + +Y = A^T x [(G x g x G^T) @ (B^T x d x B)] x A + +The size of output Y is m x m. The size of filter g is r x r. The size of +input d is (m + r - 1) x (m + r - 1). A^T, A, G^T, G, B^T, and B are +transformation matrices. + +This operator is defined to represent the high level concept of filter +transformation (G x g x G^T) in the Winograd Conv2D algorithm. +""" +function winograd_filter_transform( + filter::Value, output::Value; result::IR.Type, m, r, location=Location() +) + _results = IR.Type[result,] + _operands = Value[filter, output] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("m", m), namedattribute("r", r)] + + return IR.create_operation( + "linalg.winograd_filter_transform", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`winograd_input_transform` + +Winograd Conv2D algorithm will convert linalg Conv2D operator into batched +matrix multiply. Before the matrix multiply, it will convert filter and +input into a format suitable for batched matrix multiply. After the matrix +multiply, it will convert output to the final result tensor. + +The algorithm F(m x m, r x r) is + +Y = A^T x [(G x g x G^T) @ (B^T x d x B)] x A + +The size of output Y is m x m. The size of filter g is r x r. The size of +input d is (m + r - 1) x (m + r - 1). A^T, A, G^T, G, B^T, and B are +transformation matrices. + +This operator is defined to represent the high level concept of input +transformation (B^T x d x B) in the Winograd Conv2D algorithm. +""" +function winograd_input_transform( + input::Value, output::Value; result::IR.Type, m, r, location=Location() +) + _results = IR.Type[result,] + _operands = Value[input, output] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("m", m), namedattribute("r", r)] + + return IR.create_operation( + "linalg.winograd_input_transform", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`winograd_output_transform` + +Winograd Conv2D algorithm will convert linalg Conv2D operator into batched +matrix multiply. Before the matrix multiply, it will convert filter and +input into a format suitable for batched matrix multiply. After the matrix +multiply, it will convert output to the final result tensor. + +The algorithm F(m x m, r x r) is + +Y = A^T x [(G x g x G^T) @ (B^T x d x B)] x A + +The size of output Y is m x m. The size of filter g is r x r. The size of +input d is (m + r - 1) x (m + r - 1). A^T, A, G^T, G, B^T, and B are +transformation matrices. + +This operator is defined to represent the high level concept of output +transformation (A^T x y x A) in the Winograd Conv2D algorithm. +""" +function winograd_output_transform( + value::Value, output::Value; result::IR.Type, m, r, location=Location() +) + _results = IR.Type[result,] + _operands = Value[value, output] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("m", m), namedattribute("r", r)] + + return IR.create_operation( + "linalg.winograd_output_transform", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +`linalg.yield` is a special terminator operation for blocks inside regions +in `linalg` generic ops. It returns values to the immediately enclosing +`linalg` generic op. + +# Example + +```mlir +linalg.yield %f0, %f1 : f32, f32 +``` +""" +function yield(values::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[values...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "linalg.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`abs` +No numeric casting is performed on the input operand. +""" +function abs( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.abs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`add` +The shapes and element types must be identical. The appropriate casts, +broadcasts and reductions should be done previously to calling this op. + +This means reduction/broadcast/element cast semantics is explicit. Further +passes can take that into account when lowering this code. For example, +a `linalg.broadcast` + `linalg.add` sequence can be lowered to a +`linalg.generic` with different affine maps for the two operands. +""" +function add( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`batch_matmul` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function batch_matmul( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.batch_matmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`batch_matmul_transpose_a` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function batch_matmul_transpose_a( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.batch_matmul_transpose_a", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`batch_matmul_transpose_b` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function batch_matmul_transpose_b( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.batch_matmul_transpose_b", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`batch_matvec` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function batch_matvec( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.batch_matvec", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`batch_mmt4d` +Besides the outermost batch dimension has the same semantic as +linalg.batch_matmul, the differences from linalg.batch_matmul in the +non-batch dimensions are the same as linalg.mmt4d vs. linalg.matmul. See the +description of lingalg.mmt4d. +""" +function batch_mmt4d( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.batch_mmt4d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`batch_reduce_matmul` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function batch_reduce_matmul( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.batch_reduce_matmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`batch_vecmat` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function batch_vecmat( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.batch_vecmat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`broadcast` + +Broadcast the input into the given shape by adding `dimensions`. + +# Example +``` + %bcast = linalg.broadcast + ins(%input:tensor<16xf32>) + inits(%init:tensor<16x64xf32>) + dimensions = [1] +``` +""" +function broadcast( + input::Value, + init::Value; + result::Vector{IR.Type}, + dimensions, + region::Region, + location=Location(), +) + _results = IR.Type[result...,] + _operands = Value[input, init] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimensions", dimensions),] + + return IR.create_operation( + "linalg.broadcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ceil` +No numeric casting is performed on the input operand. +""" +function ceil( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.ceil", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_1d_ncw_fcw` +Layout: + * Input: NCW. + * Kernel: FCW. + +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_1d_ncw_fcw( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_1d_ncw_fcw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_1d_nwc_wcf` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_1d_nwc_wcf( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_1d_nwc_wcf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_1d` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_1d( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.conv_1d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_2d_nchw_fchw` +Layout: + * Input: NCHW. + * Kernel: FCHW. + +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_2d_nchw_fchw( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_2d_nchw_fchw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_2d_ngchw_fgchw` +Layout: + * Input: NGCHW. + * Kernel: FGCHW. + +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_2d_ngchw_fgchw( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_2d_ngchw_fgchw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_2d_ngchw_gfchw` +Layout: + * Input: NGCHW. + * Kernel: GFCHW. + +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_2d_ngchw_gfchw( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_2d_ngchw_gfchw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_2d_ngchw_gfchw_q` +Layout: + * Input: NGCHW. + * Kernel: GFCHW. + +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. This includes the zero +point offsets common to quantized operations. +""" +function conv_2d_ngchw_gfchw_q( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_2d_ngchw_gfchw_q", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_2d_nhwc_fhwc` +Layout: + * Input: NHWC. + * Kernel: FHWC. + +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_2d_nhwc_fhwc( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_2d_nhwc_fhwc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_2d_nhwc_fhwc_q` +Layout: + * Input: NHWC. + * Kernel: FHWC. + +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. This includes the zero +point offsets common to quantized operations. +""" +function conv_2d_nhwc_fhwc_q( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_2d_nhwc_fhwc_q", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_2d_nhwc_hwcf` +Layout: + * Input: NHWC. + * Kernel: HWCF. + +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_2d_nhwc_hwcf( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_2d_nhwc_hwcf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_2d_nhwc_hwcf_q` +Layout: + * Input: NHWC. + * Kernel: HWCF. + +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. This includes the zero +point offsets common to quantized operations. +""" +function conv_2d_nhwc_hwcf_q( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_2d_nhwc_hwcf_q", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_2d` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_2d( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.conv_2d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_3d_ncdhw_fcdhw` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_3d_ncdhw_fcdhw( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_3d_ncdhw_fcdhw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_3d_ndhwc_dhwcf` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_3d_ndhwc_dhwcf( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_3d_ndhwc_dhwcf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_3d_ndhwc_dhwcf_q` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. This includes the zero +point offsets common to quantized operations. +""" +function conv_3d_ndhwc_dhwcf_q( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.conv_3d_ndhwc_dhwcf_q", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv_3d` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function conv_3d( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.conv_3d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`copy` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function copy( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + cast=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(cast) && push!(_attributes, namedattribute("cast", cast)) + + return IR.create_operation( + "linalg.copy", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv_1d_ncw_cw` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. Multiplier is set to 1 +which is a special case for most depthwise convolutions. +""" +function depthwise_conv_1d_ncw_cw( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.depthwise_conv_1d_ncw_cw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv_1d_nwc_wc` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. Multiplier is set to 1 +which is a special case for most depthwise convolutions. +""" +function depthwise_conv_1d_nwc_wc( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.depthwise_conv_1d_nwc_wc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv_1d_nwc_wcm` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function depthwise_conv_1d_nwc_wcm( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.depthwise_conv_1d_nwc_wcm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv_2d_nchw_chw` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. Multiplier is set to 1 +which is a special case for most depthwise convolutions. +""" +function depthwise_conv_2d_nchw_chw( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.depthwise_conv_2d_nchw_chw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv_2d_nhwc_hwc` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. Multiplier is set to 1 +which is a special case for most depthwise convolutions. +""" +function depthwise_conv_2d_nhwc_hwc( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.depthwise_conv_2d_nhwc_hwc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv_2d_nhwc_hwc_q` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function depthwise_conv_2d_nhwc_hwc_q( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.depthwise_conv_2d_nhwc_hwc_q", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv_2d_nhwc_hwcm` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function depthwise_conv_2d_nhwc_hwcm( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.depthwise_conv_2d_nhwc_hwcm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv_2d_nhwc_hwcm_q` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function depthwise_conv_2d_nhwc_hwcm_q( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.depthwise_conv_2d_nhwc_hwcm_q", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv_3d_ncdhw_cdhw` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. Multiplier is set to 1 +which is a special case for most depthwise convolutions. +""" +function depthwise_conv_3d_ncdhw_cdhw( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.depthwise_conv_3d_ncdhw_cdhw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv_3d_ndhwc_dhwc` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. Multiplier is set to 1 +which is a special case for most depthwise convolutions. +""" +function depthwise_conv_3d_ndhwc_dhwc( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.depthwise_conv_3d_ndhwc_dhwc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv_3d_ndhwc_dhwcm` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function depthwise_conv_3d_ndhwc_dhwcm( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.depthwise_conv_3d_ndhwc_dhwcm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`div` +The shapes and element types must be identical. The appropriate casts, +broadcasts and reductions should be done previously to calling this op. + +This means reduction/broadcast/element cast semantics is explicit. Further +passes can take that into account when lowering this code. For example, +a `linalg.broadcast` + `linalg.div` sequence can be lowered to a +`linalg.generic` with different affine maps for the two operands. +""" +function div( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.div", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`div_unsigned` +The shapes and element types must be identical. The appropriate casts, +broadcasts and reductions should be done previously to calling this op. + +This means reduction/broadcast/element cast semantics is explicit. Further +passes can take that into account when lowering this code. For example, +a `linalg.broadcast` + `linalg.div` sequence can be lowered to a +`linalg.generic` with different affine maps for the two operands. +""" +function div_unsigned( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.div_unsigned", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`dot` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function dot( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.dot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`elemwise_binary` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function elemwise_binary( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + fun=nothing, + cast=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(fun) && push!(_attributes, namedattribute("fun", fun)) + !isnothing(cast) && push!(_attributes, namedattribute("cast", cast)) + + return IR.create_operation( + "linalg.elemwise_binary", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`elemwise_unary` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function elemwise_unary( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + fun=nothing, + cast=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(fun) && push!(_attributes, namedattribute("fun", fun)) + !isnothing(cast) && push!(_attributes, namedattribute("cast", cast)) + + return IR.create_operation( + "linalg.elemwise_unary", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`erf` +No numeric casting is performed on the input operand. +""" +function erf( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.erf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`exp` +No numeric casting is performed on the input operand. +""" +function exp( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.exp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fill` +Works for arbitrary ranked output tensors since the operation performs scalar +accesses only and is thus rank polymorphic. Numeric casting is performed on +the value operand, promoting it to the same data type as the output. +""" +function fill( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.fill", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fill_rng_2d` +The operation generations pseudo random numbers using a linear congruential +generator. It provides no guarantees regarding the distribution of the +generated random numbers. Instead of generating the random numbers +sequentially, it instantiates one random number generator per data element +and runs them in parallel. The seed operand and the indices of the data +element seed the random number generation. The min and max operands limit +the range of the generated random numbers. +""" +function fill_rng_2d( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.fill_rng_2d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`floor` +No numeric casting is performed on the input operand. +""" +function floor( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.floor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`generic` + +Generic Linalg op form where the key properties of the computation are +specified as attributes. In pretty form, a `linalg.generic` op is written +as: + + ```mlir + linalg.generic #trait_attribute + ins(%A, %B : memref, + memref) + outs(%C : memref) + attrs = {other-optional-attributes} + {region} + ``` + +Where #trait_attributes is an alias of a dictionary attribute containing: + - doc [optional]: a documentation string + - indexing_maps: a list of AffineMapAttr, one AffineMapAttr per each input + and output view. Such AffineMapAttr specifies the mapping between the + loops and the indexing within each view. + - library_call [optional]: a StringAttr containing the name of an + external library function that the linalg.generic operation maps to. + The external library is assumed to be dynamically linked and no strong + compile-time guarantees are provided. In the absence of such a library + call, linalg.generic will always lower to loops. + - iterator_types: an ArrayAttr specifying the type of the enclosing loops. + Each element of the list represents and iterator of one of the following + types: + parallel, reduction, window + +# Example +Defining a #matmul_trait attribute in MLIR can be done as follows: + ```mlir + #matmul_accesses = [ + (m, n, k) -> (m, k), + (m, n, k) -> (k, n), + (m, n, k) -> (m, n) + ] + #matmul_trait = { + doc = \"C(m, n) += A(m, k) * B(k, n)\", + indexing_maps = #matmul_accesses, + library_call = \"linalg_matmul\", + iterator_types = [\"parallel\", \"parallel\", \"reduction\"] + } + ``` + +And can be reused in multiple places as: + ```mlir + linalg.generic #matmul_trait + ins(%A, %B : memref, + memref) + outs(%C : memref) + {other-optional-attributes} { + ^bb0(%a: f32, %b: f32, %c: f32) : + %d = arith.mulf %a, %b: f32 + %e = arith.addf %c, %d: f32 + linalg.yield %e : f32 + } + ``` + +This may lower to either: + ```mlir + call @linalg_matmul(%A, %B, %C) : + (memref, + memref, + memref) + -> () + ``` + +or IR resembling: +```mlir +scf.for %m = %c0 to %M step %c1 { + scf.for %n = %c0 to %N step %c1 { + scf.for %k = %c0 to %K step %c1 { + %a = load %A[%m, %k] : memref + %b = load %B[%k, %n] : memref + %c = load %C[%m, %n] : memref + %d = arith.mulf %a, %b: f32 + %e = arith.addf %c, %d: f32 + store %e, %C[%m, %n] : memref + } + } +} +``` + +To allow progressive lowering from the value world (a.k.a tensor values) to +the buffer world (a.k.a memref values), a `linalg.generic` op allows mixing +tensors and buffers operands and tensor results. + +```mlir +%C = linalg.generic #trait_attribute + ins(%A, %B : tensor, memref) + outs(%C : tensor) + {other-optional-attributes} + {region} + -> (tensor) +``` +""" +function generic( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + indexing_maps, + iterator_types, + doc=nothing, + library_call=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("indexing_maps", indexing_maps), + namedattribute("iterator_types", iterator_types), + ] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(doc) && push!(_attributes, namedattribute("doc", doc)) + !isnothing(library_call) && + push!(_attributes, namedattribute("library_call", library_call)) + + return IR.create_operation( + "linalg.generic", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`log` +No numeric casting is performed on the input operand. +""" +function log( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.log", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`map` + +Models elementwise operations on tensors in terms of arithmetic operations +on the corresponding elements. + +# Example +``` + %add = linalg.map + ins(%lhs, %rhs : tensor<64xf32>, tensor<64xf32>) + outs(%init: tensor<64xf32>) + (%lhs_elem: f32, %rhs_elem: f32) { + %0 = arith.addf %lhs_elem, %rhs_elem: f32 + linalg.yield %0: f32 + } +``` + +Shortened print form is available. Applies to simple maps with one +non-yield operation inside the body. + +The example above will be printed as: +``` + %add = linalg.map { arith.addf } + ins(%lhs, %rhs : tensor<64xf32>, tensor<64xf32>) + outs(%init: tensor<64xf32>) +``` +""" +function map( + inputs::Vector{Value}, + init::Value; + result::Vector{IR.Type}, + mapper::Region, + location=Location(), +) + _results = IR.Type[result...,] + _operands = Value[inputs..., init] + _owned_regions = Region[mapper,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "linalg.map", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`matmul` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function matmul( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + cast=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(cast) && push!(_attributes, namedattribute("cast", cast)) + + return IR.create_operation( + "linalg.matmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`matmul_transpose_a` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function matmul_transpose_a( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + cast=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(cast) && push!(_attributes, namedattribute("cast", cast)) + + return IR.create_operation( + "linalg.matmul_transpose_a", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`matmul_transpose_b` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function matmul_transpose_b( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + cast=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(cast) && push!(_attributes, namedattribute("cast", cast)) + + return IR.create_operation( + "linalg.matmul_transpose_b", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`matvec` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function matvec( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.matvec", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`max` +The shapes and element types must be identical. The appropriate casts, +broadcasts and reductions should be done previously to calling this op. + +This means reduction/broadcast/element cast semantics is explicit. Further +passes can take that into account when lowering this code. For example, +a `linalg.broadcast` + `linalg.max` sequence can be lowered to a +`linalg.generic` with different affine maps for the two operands. +""" +function max( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.max", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`min` +The shapes and element types must be identical. The appropriate casts, +broadcasts and reductions should be done previously to calling this op. + +This means reduction/broadcast/element cast semantics is explicit. Further +passes can take that into account when lowering this code. For example, +a `linalg.broadcast` + `linalg.min` sequence can be lowered to a +`linalg.generic` with different affine maps for the two operands. +""" +function min( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.min", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mmt4d` +Differences from linalg.matmul: +* The right hand side is transposed, whence the \'t\' in \'mmt\'. +* The input and output tensors have a 4D shape instead of a 2D shape. They + are interpreted as 2D matrices with one level of 2D tile subdivision, + whence the 2+2=4 dimensions. The inner tile dimensions are identified with + \'0\' suffixes below, for instance the LHS matrix shape (M, K, M0, K0) reads + as: MxK tiles, each of shape M0xK0. +""" +function mmt4d( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.mmt4d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mul` +The shapes and element types must be identical. The appropriate casts, +broadcasts and reductions should be done previously to calling this op. + +This means reduction/broadcast/element cast semantics is explicit. Further +passes can take that into account when lowering this code. For example, +a `linalg.broadcast` + `linalg.mul` sequence can be lowered to a +`linalg.generic` with different affine maps for the two operands. +""" +function mul( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`negf` +No numeric casting is performed on the input operand. +""" +function negf( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.negf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nchw_max` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nchw_max( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nchw_max", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nchw_sum` +Layout: + * Input: NCHW. + * Kernel: HW. + +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nchw_sum( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nchw_sum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_ncw_max` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_ncw_max( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_ncw_max", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_ncw_sum` +Layout: + * Input: NCW. + * Kernel: W. + +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_ncw_sum( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_ncw_sum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_ndhwc_max` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_ndhwc_max( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_ndhwc_max", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_ndhwc_min` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_ndhwc_min( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_ndhwc_min", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_ndhwc_sum` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_ndhwc_sum( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_ndhwc_sum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nhwc_max` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nhwc_max( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nhwc_max", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nhwc_max_unsigned` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nhwc_max_unsigned( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nhwc_max_unsigned", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nhwc_min` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nhwc_min( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nhwc_min", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nhwc_min_unsigned` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nhwc_min_unsigned( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nhwc_min_unsigned", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nhwc_sum` +Layout: + * Input: NHWC. + * Kernel: HW. + +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nhwc_sum( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nhwc_sum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nwc_max` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nwc_max( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nwc_max", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nwc_max_unsigned` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nwc_max_unsigned( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nwc_max_unsigned", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nwc_min` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nwc_min( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nwc_min", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nwc_min_unsigned` +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nwc_min_unsigned( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nwc_min_unsigned", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pooling_nwc_sum` +Layout: + * Input: NWC. + * Kernel: W. + +Numeric casting is performed on the input operand, promoting it to the same +data type as the accumulator/output. +""" +function pooling_nwc_sum( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + strides=nothing, + dilations=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + !isnothing(strides) && push!(_attributes, namedattribute("strides", strides)) + !isnothing(dilations) && push!(_attributes, namedattribute("dilations", dilations)) + + return IR.create_operation( + "linalg.pooling_nwc_sum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`powf` +Only applies to floating point values. + +The shapes and element types must be identical. The appropriate casts, +broadcasts and reductions should be done previously to calling this op. + +This means reduction/broadcast/element cast semantics is explicit. Further +passes can take that into account when lowering this code. For example, +a `linalg.broadcast` + `linalg.powf` sequence can be lowered to a +`linalg.generic` with different affine maps for the two operands. +""" +function powf( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.powf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`quantized_batch_matmul` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. The quantized variant +includes zero-point adjustments for the left and right operands of the +matmul. +""" +function quantized_batch_matmul( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.quantized_batch_matmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`quantized_matmul` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. The quantized variant +includes zero-point adjustments for the left and right operands of the +matmul. +""" +function quantized_matmul( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.quantized_matmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reciprocal` +No numeric casting is performed on the input operand. +""" +function reciprocal( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.reciprocal", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reduce` + +Executes `combiner` on the `dimensions` of `inputs` and returns the +reduced result. The `dimensions` attribute needs to list the reduction +dimensions in increasing order. + +# Example +``` + %reduce = linalg.reduce + ins(%input:tensor<16x32x64xf32>) + outs(%init:tensor<16x64xf32>) + dimensions = [1] + (%in: f32, %out: f32) { + %0 = arith.addf %out, %in: f32 + linalg.yield %0: f32 + } +``` + +Shortened print form is available. Applies to simple (not variadic) reduces +with one non-yield operation inside the body. Applies only if the operation +takes `%out` as the first argument. + +The example above will be printed as: +``` + %reduce = linalg.reduce { arith.addf } + ins(%input:tensor<16x32x64xf32>) + outs(%init:tensor<16x64xf32>) + dimensions = [1] +``` +""" +function reduce( + inputs::Vector{Value}, + inits::Vector{Value}; + result_0::Vector{IR.Type}, + dimensions, + combiner::Region, + location=Location(), +) + _results = IR.Type[result_0...,] + _operands = Value[inputs..., inits...] + _owned_regions = Region[combiner,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimensions", dimensions),] + + return IR.create_operation( + "linalg.reduce", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`round` +No numeric casting is performed on the input operand. +""" +function round( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.round", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`rsqrt` +No numeric casting is performed on the input operand. +""" +function rsqrt( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.rsqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`select` +The shapes and element types must be identical. The appropriate casts, +broadcasts and reductions should be done previously to calling this op. + +This means reduction/broadcast/element cast semantics is explicit. Further +passes can take that into account when lowering this code. For example, +a `linalg.broadcast` + `linalg.select` sequence can be lowered to a +`linalg.generic` with different affine maps for the two operands. +""" +function select( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.select", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sqrt` +No numeric casting is performed on the input operand. +""" +function sqrt( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.sqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`square` +No numeric casting is performed on the input operand. +""" +function square( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.square", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sub` +The shapes and element types must be identical. The appropriate casts, +broadcasts and reductions should be done previously to calling this op. + +This means reduction/broadcast/element cast semantics is explicit. Further +passes can take that into account when lowering this code. For example, +a `linalg.broadcast` + `linalg.sub` sequence can be lowered to a +`linalg.generic` with different affine maps for the two operands. +""" +function sub( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.sub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tanh` +No numeric casting is performed on the input operand. +""" +function tanh( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.tanh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`transpose` + +Permutes the dimensions of `input` according to the given `permutation`. + `dim(result, i) = dim(input, permutation[i])` + +This op actually moves data, unlike `memref.transpose` which is a metadata +operation only that produces a transposed \"view\". + +# Example +``` + %transpose = linalg.transpose + ins(%input:tensor<16x64xf32>) + outs(%init:tensor<64x16xf32>) + permutation = [1, 0] +``` +""" +function transpose( + input::Value, + init::Value; + result::Vector{IR.Type}, + permutation, + region::Region, + location=Location(), +) + _results = IR.Type[result...,] + _operands = Value[input, init] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("permutation", permutation),] + + return IR.create_operation( + "linalg.transpose", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`vecmat` +Numeric casting is performed on the operands to the inner multiply, promoting +them to the same data type as the accumulator/output. +""" +function vecmat( + inputs::Vector{Value}, + outputs::Vector{Value}; + result_tensors::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result_tensors...,] + _operands = Value[inputs..., outputs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(inputs), length(outputs)])) + + return IR.create_operation( + "linalg.vecmat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # linalg diff --git a/src/Dialects/19/MLProgram.jl b/src/Dialects/19/MLProgram.jl new file mode 100644 index 00000000..877a3cde --- /dev/null +++ b/src/Dialects/19/MLProgram.jl @@ -0,0 +1,457 @@ +module ml_program + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`func` + +This simple function container represents callables in an ML program where +the body is an `SSACFG` region. It must be terminated by a `return` op which +yields values with the same arity and types as the `FunctionType` results +of the containing `func`. + +This op is a `Symbol` but does not introduce a new `SymbolTable`. As such, +it cannot represent nested symbols. + +# Example + +```mlir +ml_program.func private @some_extern(i32) -> i32 +ml_program.func @compute(%arg0 : i32) -> i32 { + ml_program.return %arg0 : i32 +} +``` +""" +function func(; + sym_name, + function_type, + arg_attrs=nothing, + res_attrs=nothing, + sym_visibility=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("function_type", function_type) + ] + !isnothing(arg_attrs) && push!(_attributes, namedattribute("arg_attrs", arg_attrs)) + !isnothing(res_attrs) && push!(_attributes, namedattribute("res_attrs", res_attrs)) + !isnothing(sym_visibility) && + push!(_attributes, namedattribute("sym_visibility", sym_visibility)) + + return IR.create_operation( + "ml_program.func", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`global_load_const` + +Loads a constant (immutable) value from a global directly by symbol. + +This op is only legal for globals that are not mutable and exists because +such a load can be considered to have no side effects. + +# Example + +```mlir +%0 = ml_program.global_load_const @foobar : tensor +``` +""" +function global_load_const(; result::IR.Type, global_, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("global", global_),] + + return IR.create_operation( + "ml_program.global_load_const", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`global_load_graph` + +Performs a non-atomic, non-volatile, non-synchronized load from a global +that may be mutable. + +It is fully expected that these constraints are not suitable for all +situations, and alternative ops should be defined and used for more advanced +cases. + +This op is side effecting and may not be valid to use in graph regions +without additional consideration to evaluation order constraints. + +# Example + +```mlir +%0, %cstr = ml_program.global_load_graph @foobar + ordering (%token -> !ml_program.token) : tensor +``` +""" +function global_load_graph( + consumeTokens::Vector{Value}; + result::IR.Type, + produceToken::IR.Type, + global_, + location=Location(), +) + _results = IR.Type[result, produceToken] + _operands = Value[consumeTokens...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("global", global_),] + + return IR.create_operation( + "ml_program.global_load_graph", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`global_load` + +Performs a non-atomic, non-volatile, non-synchronized load from a global +that may be mutable. + +It is fully expected that these constraints are not suitable for +all situations, and alternative ops should be defined and used for more +advanced cases. + +This op is side effecting and may not be valid to use in graph regions +without additional consideration to evaluation order constraints. See +`global_load_graph` for op which allows for explicit ordering constraints. + +# Example + +```mlir +%0 = ml_program.global_load @foobar : tensor +``` +""" +function global_load(; result::IR.Type, global_, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("global", global_),] + + return IR.create_operation( + "ml_program.global_load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`global_` + +Declares a named global variable (or constant). + +A global contains a value of a specified type which can be accessed at +runtime via appropriate load/store operations. It can be mutable or +constant, optionally taking an initial value or declared as +extern (in which case, the initial value is found in external storage +by symbol name). + +Generally, the type of the global and the type of the initial value +will be the same. However, for type hierarchies which can have a more +generalized bounding type that can be assigned from a narrow type, this +is allowed (but not verified). + +Examples: + +```mlir +// Constant global. +ml_program.global @foobar(dense<4> : tensor<4xi32>) : tensor + +// Constant with external linkage. +ml_program.global mutable @foobar(#ml_program.extern>) + : tensor + +// Mutable global with an undefined initial value. +ml_program.global mutable @foobar : tensor +``` +""" +function global_(; + sym_name, + type, + is_mutable=nothing, + value=nothing, + sym_visibility=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("type", type) + ] + !isnothing(is_mutable) && push!(_attributes, namedattribute("is_mutable", is_mutable)) + !isnothing(value) && push!(_attributes, namedattribute("value", value)) + !isnothing(sym_visibility) && + push!(_attributes, namedattribute("sym_visibility", sym_visibility)) + + return IR.create_operation( + "ml_program.global", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`global_store_graph` + +Performs a non-atomic, non-volatile, non-synchronized store to a mutable +global. + +It is fully expected that these constraints are not suitable for +all situations, and alternative ops should be defined and used for more +advanced cases. + +This op is side effecting and may not be valid to use in graph regions +without additional consideration to evaluation order constraints. + +# Example + +```mlir +%token = ml_program.global_store @foobar = %0 : tensor + ordering (%in_token -> !ml_program.token) : tensor +``` +""" +function global_store_graph( + value::Value, + consumeTokens::Vector{Value}; + produceToken::IR.Type, + global_, + location=Location(), +) + _results = IR.Type[produceToken,] + _operands = Value[value, consumeTokens...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("global", global_),] + + return IR.create_operation( + "ml_program.global_store_graph", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`global_store` + +Performs a non-atomic, non-volatile, non-synchronized store to a mutable +global. + +It is fully expected that these constraints are not suitable for +all situations, and alternative ops should be defined and used for more +advanced cases. + +This op is side effecting and may not be valid to use in graph regions +without additional consideration to evaluation order constraints. See +`global_store_graph` for op which allows for explicit ordering constraints. + +# Example + +```mlir +ml_program.global_store @foobar = %0 : tensor +``` +""" +function global_store(value::Value; global_, location=Location()) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("global", global_),] + + return IR.create_operation( + "ml_program.global_store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`output` + +The `output` operation terminates a subgraph by yielding values +to the caller. +The operation takes variable number of operands and produces no results. +The operand number and types must match the signature of the function +that contains the operation. +""" +function output(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "ml_program.output", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`return_` + +The `return` operation terminates a `func` function by yielding values +to the caller. +The operation takes variable number of operands and produces no results. +The operand number and types must match the signature of the function +that contains the operation. +""" +function return_(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "ml_program.return", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`subgraph` + +This simple function container represents callables in an ML program where +the body is a `Graph` region containing a single block. It must be +terminated by an `output` op which yields values with the same arity and +types as the `FunctionType` results of the containing `subgraph`. + +This op is a `Symbol` but does not introduce a new `SymbolTable`. As such, +it cannot represented nested symbols. + +# Example + +```mlir +ml_program.subgraph private @some_extern(i32) -> i32 +ml_program.subgraph @compute(%arg0 : i32) -> i32 { + ml_program.output %arg0 : i32 +} +``` +""" +function subgraph(; + sym_name, + function_type, + arg_attrs=nothing, + res_attrs=nothing, + sym_visibility=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("function_type", function_type) + ] + !isnothing(arg_attrs) && push!(_attributes, namedattribute("arg_attrs", arg_attrs)) + !isnothing(res_attrs) && push!(_attributes, namedattribute("res_attrs", res_attrs)) + !isnothing(sym_visibility) && + push!(_attributes, namedattribute("sym_visibility", sym_visibility)) + + return IR.create_operation( + "ml_program.subgraph", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`token` + +Token values are used to chain side effecting ops in a graph so as to +establish an execution order. This op produces a token. +""" +function token(; token::IR.Type, location=Location()) + _results = IR.Type[token,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "ml_program.token", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # ml_program diff --git a/src/Dialects/19/MPI.jl b/src/Dialects/19/MPI.jl new file mode 100644 index 00000000..36ed8d7b --- /dev/null +++ b/src/Dialects/19/MPI.jl @@ -0,0 +1,227 @@ +module mpi + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`comm_rank` + +Communicators other than `MPI_COMM_WORLD` are not supported for now. + +This operation can optionally return an `!mpi.retval` value that can be used +to check for errors. +""" +function comm_rank(; + retval=nothing::Union{Nothing,IR.Type}, rank::IR.Type, location=Location() +) + _results = IR.Type[rank,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(retval) && push!(_results, retval) + + return IR.create_operation( + "mpi.comm_rank", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`error_class` + +`MPI_Error_class` maps return values from MPI calls to a set of well-known +MPI error classes. +""" +function error_class(val::Value; errclass::IR.Type, location=Location()) + _results = IR.Type[errclass,] + _operands = Value[val,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "mpi.error_class", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`finalize` + +This function cleans up the MPI state. Afterwards, no MPI methods may +be invoked (excpet for MPI_Get_version, MPI_Initialized, and MPI_Finalized). +Notably, MPI_Init cannot be called again in the same program. + +This operation can optionally return an `!mpi.retval` value that can be used +to check for errors. +""" +function finalize(; retval=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(retval) && push!(_results, retval) + + return IR.create_operation( + "mpi.finalize", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`init` + +This operation must preceed most MPI calls (except for very few exceptions, +please consult with the MPI specification on these). + +Passing &argc, &argv is not supported currently. + +This operation can optionally return an `!mpi.retval` value that can be used +to check for errors. +""" +function init(; retval=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(retval) && push!(_results, retval) + + return IR.create_operation( + "mpi.init", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`recv` + +MPI_Recv performs a blocking receive of `size` elements of type `dtype` +from rank `dest`. The `tag` value and communicator enables the library to +determine the matching of multiple sends and receives between the same +ranks. + +Communicators other than `MPI_COMM_WORLD` are not supprted for now. +The MPI_Status is set to `MPI_STATUS_IGNORE`, as the status object +is not yet ported to MLIR. + +This operation can optionally return an `!mpi.retval` value that can be used +to check for errors. +""" +function recv( + ref::Value, + tag::Value, + rank::Value; + retval=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ref, tag, rank] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(retval) && push!(_results, retval) + + return IR.create_operation( + "mpi.recv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`retval_check` + +This operation compares MPI status codes to known error class +constants such as `MPI_SUCCESS`, or `MPI_ERR_COMM`. +""" +function retval_check(val::Value; res::IR.Type, errclass, location=Location()) + _results = IR.Type[res,] + _operands = Value[val,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("errclass", errclass),] + + return IR.create_operation( + "mpi.retval_check", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`send` + +MPI_Send performs a blocking send of `size` elements of type `dtype` to rank +`dest`. The `tag` value and communicator enables the library to determine +the matching of multiple sends and receives between the same ranks. + +Communicators other than `MPI_COMM_WORLD` are not supprted for now. + +This operation can optionally return an `!mpi.retval` value that can be used +to check for errors. +""" +function send( + ref::Value, + tag::Value, + rank::Value; + retval=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ref, tag, rank] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(retval) && push!(_results, retval) + + return IR.create_operation( + "mpi.send", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # mpi diff --git a/src/Dialects/19/Math.jl b/src/Dialects/19/Math.jl new file mode 100644 index 00000000..ffacc8cb --- /dev/null +++ b/src/Dialects/19/Math.jl @@ -0,0 +1,1605 @@ +module math + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`absf` + +The `absf` operation computes the absolute value. It takes one operand of +floating point type (i.e., scalar, tensor or vector) and returns one result +of the same type. + +# Example + +```mlir +// Scalar absolute value. +%a = math.absf %b : f64 +``` +""" +function absf( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.absf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`absi` + +The `absi` operation computes the absolute value. It takes one operand of +integer type (i.e., scalar, tensor or vector) and returns one result of the +same type. + +# Example + +```mlir +// Scalar absolute value. +%a = math.absi %b : i64 +``` +""" +function absi(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "math.absi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`acos` + +The `acos` operation computes the arcus cosine of a given value. It takes one +operand of floating point type (i.e., scalar, tensor or vector) and returns one +result of the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar arcus cosine value. +%a = math.acos %b : f64 +``` +""" +function acos( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.acos", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`acosh` + +# Syntax + +``` +operation ::= ssa-id `=` `math.acosh` ssa-use `:` type +``` + +The `acosh` operation computes the arcus cosine of a given value. It takes +one operand of floating point type (i.e., scalar, tensor or vector) and returns +one result of the same type. It has no standard attributes. + +# Example + +```mlir +// Hyperbolic arcus cosine of scalar value. +%a = math.acosh %b : f64 +``` +""" +function acosh( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.acosh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`asin` + +# Syntax + +``` +operation ::= ssa-id `=` `math.asin` ssa-use `:` type +``` + +The `asin` operation computes the arcus sine of a given value. It takes +one operand of floating point type (i.e., scalar, tensor or vector) and returns +one result of the same type. It has no standard attributes. + +# Example + +```mlir +// Arcus sine of scalar value. +%a = math.asin %b : f64 +``` +""" +function asin( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.asin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`asinh` + +# Syntax + +``` +operation ::= ssa-id `=` `math.asinh` ssa-use `:` type +``` + +The `asinh` operation computes the hyperbolic arcus sine of a given value. It takes +one operand of floating point type (i.e., scalar, tensor or vector) and returns +one result of the same type. It has no standard attributes. + +# Example + +```mlir +// Hyperbolic arcus sine of scalar value. +%a = math.asinh %b : f64 +``` +""" +function asinh( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.asinh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`atan2` + +The `atan2` operation takes two operands and returns one result, all of +which must be of the same type. The operands must be of floating point type +(i.e., scalar, tensor or vector). + +The 2-argument arcus tangent `atan2(y, x)` returns the angle in the +Euclidian plane between the positive x-axis and the ray through the point +(x, y). It is a generalization of the 1-argument arcus tangent which +returns the angle on the basis of the ratio y/x. + +See also https://en.wikipedia.org/wiki/Atan2 + +# Example + +```mlir +// Scalar variant. +%a = math.atan2 %b, %c : f32 +``` +""" +function atan2( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.atan2", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`atan` + +The `atan` operation computes the arcus tangent of a given value. It takes +one operand of floating point type (i.e., scalar, tensor or vector) and returns +one result of the same type. It has no standard attributes. + +# Example + +```mlir +// Arcus tangent of scalar value. +%a = math.atan %b : f64 +``` +""" +function atan( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.atan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`atanh` + +# Syntax + +``` +operation ::= ssa-id `=` `math.atanh` ssa-use `:` type +``` + +The `atanh` operation computes the hyperbolic arcus tangent of a given value. It takes +one operand of floating point type (i.e., scalar, tensor or vector) and returns +one result of the same type. It has no standard attributes. + +# Example + +```mlir +// Hyperbolic arcus tangent of scalar value. +%a = math.atanh %b : f64 +``` +""" +function atanh( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.atanh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cbrt` + +The `cbrt` operation computes the cube root. It takes one operand of +floating point type (i.e., scalar, tensor or vector) and returns one result +of the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar cube root value. +%a = math.cbrt %b : f64 +``` + +Note: This op is not equivalent to powf(..., 1/3.0). +""" +function cbrt( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.cbrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ceil` + +The `ceil` operation computes the ceiling of a given value. It takes one +operand of floating point type (i.e., scalar, tensor or vector) and returns one +result of the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar ceiling value. +%a = math.ceil %b : f64 +``` +""" +function ceil( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.ceil", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`copysign` + +The `copysign` returns a value with the magnitude of the first operand and +the sign of the second operand. It takes two operands and returns one result of +the same type. The operands must be of floating point type (i.e., scalar, +tensor or vector). It has no standard attributes. + +# Example + +```mlir +// Scalar copysign value. +%a = math.copysign %b, %c : f64 +``` +""" +function copysign( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.copysign", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cos` + +The `cos` operation computes the cosine of a given value. It takes one +operand of floating point type (i.e., scalar, tensor or vector) and returns one +result of the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar cosine value. +%a = math.cos %b : f64 +``` +""" +function cos( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.cos", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cosh` + +The `cosh` operation computes the hyperbolic cosine. It takes one operand +of floating point type (i.e., scalar, tensor or vector) and returns one +result of the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar hyperbolic cosine value. +%a = math.cosh %b : f64 +``` +""" +function cosh( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.cosh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ctlz` + +The `ctlz` operation computes the number of leading zeros of an integer value. +It operates on scalar, tensor or vector. + +# Example + +```mlir +// Scalar ctlz function value. +%a = math.ctlz %b : i32 +``` +""" +function ctlz(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "math.ctlz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cttz` + +The `cttz` operation computes the number of trailing zeros of an integer value. +It operates on scalar, tensor or vector. + +# Example + +```mlir +// Scalar cttz function value. +%a = math.cttz %b : i32 +``` +""" +function cttz(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "math.cttz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ctpop` + +The `ctpop` operation computes the number of set bits of an integer value. +It operates on scalar, tensor or vector. + +# Example + +```mlir +// Scalar ctpop function value. +%a = math.ctpop %b : i32 +``` +""" +function ctpop(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "math.ctpop", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`erf` + +The `erf` operation computes the error function. It takes one operand of +floating point type (i.e., scalar, tensor or vector) and returns one result of +the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar error function value. +%a = math.erf %b : f64 +``` +""" +function erf( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.erf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`exp2` + +The `exp` operation takes one operand of floating point type (i.e., scalar, +tensor or vector) and returns one result of the same type. It has no standard +attributes. + +# Example + +```mlir +// Scalar natural exponential. +%a = math.exp2 %b : f64 +``` +""" +function exp2( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.exp2", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`expm1` + +expm1(x) := exp(x) - 1 + +The `expm1` operation takes one operand of floating point type (i.e., +scalar, tensor or vector) and returns one result of the same type. It has no +standard attributes. + +# Example + +```mlir +// Scalar natural exponential minus 1. +%a = math.expm1 %b : f64 +``` +""" +function expm1( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.expm1", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`exp` + +The `exp` operation takes one operand of floating point type (i.e., scalar, +tensor or vector) and returns one result of the same type. It has no standard +attributes. + +# Example + +```mlir +// Scalar natural exponential. +%a = math.exp %b : f64 +``` +""" +function exp( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.exp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`fpowi` + +The `fpowi` operation takes a `base` operand of floating point type +(i.e. scalar, tensor or vector) and a `power` operand of integer type +(also scalar, tensor or vector) and returns one result of the same type +as `base`. The result is `base` raised to the power of `power`. +The operation is elementwise for non-scalars, e.g.: + +```mlir +%v = math.fpowi %base, %power : vector<2xf32>, vector<2xi32 +``` + +The result is a vector of: + +``` +[, ] +``` + +# Example + +```mlir +// Scalar exponentiation. +%a = math.fpowi %base, %power : f64, i32 +``` +""" +function fpowi( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.fpowi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`floor` + +The `floor` operation computes the floor of a given value. It takes one +operand of floating point type (i.e., scalar, tensor or vector) and returns one +result of the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar floor value. +%a = math.floor %b : f64 +``` +""" +function floor( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.floor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`fma` + +The `fma` operation takes three operands and returns one result, each of +these is required to be the same type. Operands must be of floating point type +(i.e., scalar, tensor or vector). + +# Example + +```mlir +// Scalar fused multiply-add: d = a*b + c +%d = math.fma %a, %b, %c : f64 +``` + +The semantics of the operation correspond to those of the `llvm.fma` +[intrinsic](https://llvm.org/docs/LangRef.html#llvm-fma-intrinsic). In the +particular case of lowering to LLVM, this is guaranteed to lower +to the `llvm.fma.*` intrinsic. +""" +function fma( + a::Value, + b::Value, + c::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b, c] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.fma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ipowi` + +The `ipowi` operation takes two operands of integer type (i.e., scalar, +tensor or vector) and returns one result of the same type. Operands +must have the same type. + +# Example + +```mlir +// Scalar signed integer exponentiation. +%a = math.ipowi %b, %c : i32 +``` +""" +function ipowi( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "math.ipowi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`log1p` + +Computes the base-e logarithm of one plus the given value. It takes one +operand of floating point type (i.e., scalar, tensor or vector) and returns one +result of the same type. + +log1p(x) := log(1 + x) + +# Example + +```mlir +// Scalar log1p operation. +%y = math.log1p %x : f64 +``` +""" +function log1p( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.log1p", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`log2` + +Computes the base-2 logarithm of the given value. It takes one operand of +floating point type (i.e., scalar, tensor or vector) and returns one result of +the same type. + +# Example + +```mlir +// Scalar log2 operation. +%y = math.log2 %x : f64 +``` +""" +function log2( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.log2", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`log10` + +Computes the base-10 logarithm of the given value. It takes one operand of +floating point type (i.e., scalar, tensor or vector) and returns one result of +the same type. + +# Example + +```mlir +// Scalar log10 operation. +%y = math.log10 %x : f64 +``` +""" +function log10( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.log10", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`log` + +Computes the base-e logarithm of the given value. It takes one operand of +floating point type (i.e., scalar, tensor or vector) and returns one result of +the same type. + +# Example + +```mlir +// Scalar log operation. +%y = math.log %x : f64 +``` +""" +function log( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.log", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`powf` + +The `powf` operation takes two operands of floating point type (i.e., +scalar, tensor or vector) and returns one result of the same type. Operands +must have the same type. + +# Example + +```mlir +// Scalar exponentiation. +%a = math.powf %b, %c : f64 +``` +""" +function powf( + lhs::Value, + rhs::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.powf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`roundeven` + +The `roundeven` operation returns the operand rounded to the nearest integer +value in floating-point format. It takes one operand of floating point type +(i.e., scalar, tensor or vector) and produces one result of the same type. The +operation rounds the argument to the nearest integer value in floating-point +format, rounding halfway cases to even, regardless of the current +rounding direction. + +# Example + +```mlir +// Scalar round operation. +%a = math.roundeven %b : f64 +``` +""" +function roundeven( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.roundeven", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`round` + +The `round` operation returns the operand rounded to the nearest integer +value in floating-point format. It takes one operand of floating point type +(i.e., scalar, tensor or vector) and produces one result of the same type. The +operation rounds the argument to the nearest integer value in floating-point +format, rounding halfway cases away from zero, regardless of the current +rounding direction. + +# Example + +```mlir +// Scalar round operation. +%a = math.round %b : f64 +``` +""" +function round( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.round", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`rsqrt` + +The `rsqrt` operation computes the reciprocal of the square root. It takes +one operand of floating point type (i.e., scalar, tensor or vector) and returns +one result of the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar reciprocal square root value. +%a = math.rsqrt %b : f64 +``` +""" +function rsqrt( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.rsqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sin` + +The `sin` operation computes the sine of a given value. It takes one +operand of floating point type (i.e., scalar, tensor or vector) and returns one +result of the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar sine value. +%a = math.sin %b : f64 +``` +""" +function sin( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.sin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sinh` + +The `sinh` operation computes the hyperbolic sine. It takes one operand +of floating point type (i.e., scalar, tensor or vector) and returns one +result of the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar hyperbolic sine value. +%a = math.sinh %b : f64 +``` +""" +function sinh( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.sinh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sqrt` + +The `sqrt` operation computes the square root. It takes one operand of +floating point type (i.e., scalar, tensor or vector) and returns one result of +the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar square root value. +%a = math.sqrt %b : f64 +``` +""" +function sqrt( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.sqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`tan` + +The `tan` operation computes the tangent. It takes one operand +of floating point type (i.e., scalar, tensor or vector) and returns one +result of the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar tangent value. +%a = math.tan %b : f64 +``` +""" +function tan( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.tan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`tanh` + +The `tanh` operation computes the hyperbolic tangent. It takes one operand +of floating point type (i.e., scalar, tensor or vector) and returns one +result of the same type. It has no standard attributes. + +# Example + +```mlir +// Scalar hyperbolic tangent value. +%a = math.tanh %b : f64 +``` +""" +function tanh( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.tanh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`trunc` + +The `trunc` operation returns the operand rounded to the nearest integer +value in floating-point format. It takes one operand of floating point type +(i.e., scalar, tensor or vector) and produces one result of the same type. +The operation always rounds to the nearest integer not larger in magnitude +than the operand, regardless of the current rounding direction. + +# Example + +```mlir +// Scalar trunc operation. +%a = math.trunc %b : f64 +``` +""" +function trunc( + operand::Value; + result=nothing::Union{Nothing,IR.Type}, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "math.trunc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +end # math diff --git a/src/Dialects/19/MemRef.jl b/src/Dialects/19/MemRef.jl new file mode 100644 index 00000000..393e79f9 --- /dev/null +++ b/src/Dialects/19/MemRef.jl @@ -0,0 +1,1755 @@ +module memref + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`assume_alignment` + +The `assume_alignment` operation takes a memref and an integer of alignment +value, and internally annotates the buffer with the given alignment. If +the buffer isn\'t aligned to the given alignment, the behavior is undefined. + +This operation doesn\'t affect the semantics of a correct program. It\'s for +optimization only, and the optimization is best-effort. +""" +function assume_alignment(memref::Value; alignment, location=Location()) + _results = IR.Type[] + _operands = Value[memref,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("alignment", alignment),] + + return IR.create_operation( + "memref.assume_alignment", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`atomic_rmw` + +The `memref.atomic_rmw` operation provides a way to perform a read-modify-write +sequence that is free from data races. The kind enumeration specifies the +modification to perform. The value operand represents the new value to be +applied during the modification. The memref operand represents the buffer +that the read and write will be performed against, as accessed by the +specified indices. The arity of the indices is the rank of the memref. The +result represents the latest value that was stored. + +# Example + +```mlir +%x = memref.atomic_rmw \"addf\" %value, %I[%i] : (f32, memref<10xf32>) -> f32 +``` +""" +function atomic_rmw( + value::Value, + memref::Value, + indices::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + kind, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("kind", kind),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "memref.atomic_rmw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`atomic_yield` + +\"memref.atomic_yield\" yields an SSA value from a +GenericAtomicRMWOp region. +""" +function atomic_yield(result::Value; location=Location()) + _results = IR.Type[] + _operands = Value[result,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.atomic_yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`copy` + +Copies the data from the source to the destination memref. + +Usage: + +```mlir +memref.copy %arg0, %arg1 : memref to memref +``` + +Source and destination are expected to have the same element type and shape. +Otherwise, the result is undefined. They may have different layouts. +""" +function copy(source::Value, target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[source, target] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.copy", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`generic_atomic_rmw` + +The `memref.generic_atomic_rmw` operation provides a way to perform a +read-modify-write sequence that is free from data races. The memref operand +represents the buffer that the read and write will be performed against, as +accessed by the specified indices. The arity of the indices is the rank of +the memref. The result represents the latest value that was stored. The +region contains the code for the modification itself. The entry block has +a single argument that represents the value stored in `memref[indices]` +before the write is performed. No side-effecting ops are allowed in the +body of `GenericAtomicRMWOp`. + +# Example + +```mlir +%x = memref.generic_atomic_rmw %I[%i] : memref<10xf32> { + ^bb0(%current_value : f32): + %c1 = arith.constant 1.0 : f32 + %inc = arith.addf %c1, %current_value : f32 + memref.atomic_yield %inc : f32 +} +``` +""" +function generic_atomic_rmw( + memref::Value, + indices::Vector{Value}; + result::IR.Type, + atomic_body::Region, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[memref, indices...] + _owned_regions = Region[atomic_body,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.generic_atomic_rmw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`load` + +The `load` op reads an element from a memref specified by an index list. The +output of load is a new value with the same type as the elements of the +memref. The arity of indices is the rank of the memref (i.e., if the memref +loaded from is of rank 3, then 3 indices are required for the load following +the memref identifier). + +In an `affine.if` or `affine.for` body, the indices of a load are restricted +to SSA values bound to surrounding loop induction variables, +[symbols](Affine.md/#dimensions-and-symbols), results of a +constant operations, or the result of an +`affine.apply` operation that can in turn take as arguments all of the +aforementioned SSA values or the recursively result of such an +`affine.apply` operation. + +# Example + +```mlir +%1 = affine.apply affine_map<(d0, d1) -> (3*d0)> (%i, %j) +%2 = affine.apply affine_map<(d0, d1) -> (d1+1)> (%i, %j) +%12 = memref.load %A[%1, %2] : memref<8x?xi32, #layout, memspace0> + +// Example of an indirect load (treated as non-affine) +%3 = affine.apply affine_map<(d0) -> (2*d0 + 1)>(%12) +%13 = memref.load %A[%3, %2] : memref<4x?xi32, #layout, memspace0> +``` + +**Context:** The `load` and `store` operations are specifically crafted to +fully resolve a reference to an element of a memref, and (in affine +`affine.if` and `affine.for` operations) the compiler can follow use-def +chains (e.g. through [`affine.apply`](Affine.md/#affineapply-affineapplyop) +operations) to precisely analyze references at compile-time using polyhedral +techniques. This is possible because of the +[restrictions on dimensions and symbols](Affine.md/#restrictions-on-dimensions-and-symbols) +in these contexts. +""" +function load( + memref::Value, + indices::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + nontemporal=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(nontemporal) && + push!(_attributes, namedattribute("nontemporal", nontemporal)) + + return IR.create_operation( + "memref.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`alloc` + +The `alloc` operation allocates a region of memory, as specified by its +memref type. + +# Example + +```mlir +%0 = memref.alloc() : memref<8x64xf32, 1> +``` + +The optional list of dimension operands are bound to the dynamic dimensions +specified in its memref type. In the example below, the ssa value \'%d\' is +bound to the second dimension of the memref (which is dynamic). + +```mlir +%0 = memref.alloc(%d) : memref<8x?xf32, 1> +``` + +The optional list of symbol operands are bound to the symbols of the +memrefs affine map. In the example below, the ssa value \'%s\' is bound to +the symbol \'s0\' in the affine map specified in the allocs memref type. + +```mlir +%0 = memref.alloc()[%s] : memref<8x64xf32, + affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1> +``` + +This operation returns a single ssa value of memref type, which can be used +by subsequent load and store operations. + +The optional `alignment` attribute may be specified to ensure that the +region of memory that will be indexed is aligned at the specified byte +boundary. + +```mlir +%0 = memref.alloc()[%s] {alignment = 8} : + memref<8x64xf32, affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1> +``` +""" +function alloc( + dynamicSizes::Vector{Value}, + symbolOperands::Vector{Value}; + memref::IR.Type, + alignment=nothing, + location=Location(), +) + _results = IR.Type[memref,] + _operands = Value[dynamicSizes..., symbolOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(dynamicSizes), length(symbolOperands)])) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + + return IR.create_operation( + "memref.alloc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`alloca` + +The `alloca` operation allocates memory on the stack, to be automatically +released when control transfers back from the region of its closest +surrounding operation with an +[`AutomaticAllocationScope`](../Traits.md/#automaticallocationscope) trait. +The amount of memory allocated is specified by its memref and additional +operands. For example: + +```mlir +%0 = memref.alloca() : memref<8x64xf32> +``` + +The optional list of dimension operands are bound to the dynamic dimensions +specified in its memref type. In the example below, the SSA value \'%d\' is +bound to the second dimension of the memref (which is dynamic). + +```mlir +%0 = memref.alloca(%d) : memref<8x?xf32> +``` + +The optional list of symbol operands are bound to the symbols of the +memref\'s affine map. In the example below, the SSA value \'%s\' is bound to +the symbol \'s0\' in the affine map specified in the allocs memref type. + +```mlir +%0 = memref.alloca()[%s] : memref<8x64xf32, + affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>> +``` + +This operation returns a single SSA value of memref type, which can be used +by subsequent load and store operations. An optional alignment attribute, if +specified, guarantees alignment at least to that boundary. If not specified, +an alignment on any convenient boundary compatible with the type will be +chosen. +""" +function alloca( + dynamicSizes::Vector{Value}, + symbolOperands::Vector{Value}; + memref::IR.Type, + alignment=nothing, + location=Location(), +) + _results = IR.Type[memref,] + _operands = Value[dynamicSizes..., symbolOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([length(dynamicSizes), length(symbolOperands)])) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + + return IR.create_operation( + "memref.alloca", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`alloca_scope` + +The `memref.alloca_scope` operation represents an explicitly-delimited +scope for the alloca allocations. Any `memref.alloca` operations that are +used within this scope are going to be cleaned up automatically once +the control-flow exits the nested region. For example: + +```mlir +memref.alloca_scope { + %myalloca = memref.alloca(): memref<4x3xf32> + ... +} +``` + +Here, `%myalloca` memref is valid within the explicitly delimited scope +and is automatically deallocated at the end of the given region. Conceptually, +`memref.alloca_scope` is a passthrough operation with +`AutomaticAllocationScope` that spans the body of the region within the operation. + +`memref.alloca_scope` may also return results that are defined in the nested +region. To return a value, one should use `memref.alloca_scope.return` +operation: + +```mlir +%result = memref.alloca_scope { + ... + memref.alloca_scope.return %value +} +``` + +If `memref.alloca_scope` returns no value, the `memref.alloca_scope.return ` can +be left out, and will be inserted implicitly. +""" +function alloca_scope(; results::Vector{IR.Type}, bodyRegion::Region, location=Location()) + _results = IR.Type[results...,] + _operands = Value[] + _owned_regions = Region[bodyRegion,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.alloca_scope", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`alloca_scope_return` + +`memref.alloca_scope.return` operation returns zero or more SSA values +from the region within `memref.alloca_scope`. If no values are returned, +the return operation may be omitted. Otherwise, it has to be present +to indicate which values are going to be returned. For example: + +```mlir +memref.alloca_scope.return %value +``` +""" +function alloca_scope_return(results::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[results...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.alloca_scope.return", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cast` + +The `memref.cast` operation converts a memref from one type to an equivalent +type with a compatible shape. The source and destination types are +compatible if: + +a. Both are ranked memref types with the same element type, address space, +and rank and: + 1. Both have the same layout or both have compatible strided layouts. + 2. The individual sizes (resp. offset and strides in the case of strided + memrefs) may convert constant dimensions to dynamic dimensions and + vice-versa. + +If the cast converts any dimensions from an unknown to a known size, then it +acts as an assertion that fails at runtime if the dynamic dimensions +disagree with resultant destination size. + +# Example + +```mlir +// Assert that the input dynamic shape matches the destination static shape. +%2 = memref.cast %1 : memref to memref<4x4xf32> +// Erase static shape information, replacing it with dynamic information. +%3 = memref.cast %1 : memref<4xf32> to memref + +// The same holds true for offsets and strides. + +// Assert that the input dynamic shape matches the destination static stride. +%4 = memref.cast %1 : memref<12x4xf32, strided<[?, ?], offset: ?>> to + memref<12x4xf32, strided<[4, 1], offset: 5>> +// Erase static offset and stride information, replacing it with +// dynamic information. +%5 = memref.cast %1 : memref<12x4xf32, strided<[4, 1], offset: 5>> to + memref<12x4xf32, strided<[?, ?], offset: ?>> +``` + +b. Either or both memref types are unranked with the same element type, and +address space. + +# Example + +```mlir +Cast to concrete shape. + %4 = memref.cast %1 : memref<*xf32> to memref<4x?xf32> + +Erase rank information. + %5 = memref.cast %1 : memref<4x?xf32> to memref<*xf32> +``` +""" +function cast(source::Value; dest::IR.Type, location=Location()) + _results = IR.Type[dest,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`collapse_shape` + +The `memref.collapse_shape` op produces a new view with a smaller rank +whose sizes are a reassociation of the original `view`. The operation is +limited to such reassociations, where subsequent, contiguous dimensions are +collapsed into a single dimension. Such reassociations never require +additional allocs or copies. + +Collapsing non-contiguous dimensions is undefined behavior. When a group of +dimensions can be statically proven to be non-contiguous, collapses of such +groups are rejected in the verifier on a best-effort basis. In the general +case, collapses of dynamically-sized dims with dynamic strides cannot be +proven to be contiguous or non-contiguous due to limitations in the memref +type. + +A reassociation is defined as a continuous grouping of dimensions and is +represented with an array of DenseI64ArrayAttr attribute. + +Note: Only the dimensions within a reassociation group must be contiguous. +The remaining dimensions may be non-contiguous. + +The result memref type can be zero-ranked if the source memref type is +statically shaped with all dimensions being unit extent. In such a case, the +reassociation indices must be empty. + +Examples: + +```mlir +// Dimension collapse (i, j) -> i\' and k -> k\' +%1 = memref.collapse_shape %0 [[0, 1], [2]] : + memref into memref +``` + +For simplicity, this op may not be used to cast dynamicity of dimension +sizes and/or strides. I.e., a result dimension must be dynamic if and only +if at least one dimension in the corresponding reassociation group is +dynamic. Similarly, the stride of a result dimension must be dynamic if and +only if the corresponding start dimension in the source type is dynamic. + +Note: This op currently assumes that the inner strides are of the +source/result layout map are the faster-varying ones. +""" +function collapse_shape(src::Value; result::IR.Type, reassociation, location=Location()) + _results = IR.Type[result,] + _operands = Value[src,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("reassociation", reassociation),] + + return IR.create_operation( + "memref.collapse_shape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`dealloc` + +The `dealloc` operation frees the region of memory referenced by a memref +which was originally created by the `alloc` operation. +The `dealloc` operation should not be called on memrefs which alias an +alloc\'d memref (e.g. memrefs returned by `view` operations). + +# Example + +```mlir +%0 = memref.alloc() : memref<8x64xf32, affine_map<(d0, d1) -> (d0, d1), 1>> +memref.dealloc %0 : memref<8x64xf32, affine_map<(d0, d1) -> (d0, d1), 1>> +``` +""" +function dealloc(memref::Value; location=Location()) + _results = IR.Type[] + _operands = Value[memref,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.dealloc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`dim` + +The `dim` operation takes a memref and a dimension operand of type `index`. +It returns the size of the requested dimension of the given memref. +If the dimension index is out of bounds the behavior is undefined. + +The specified memref type is that of the first operand. + +# Example + +```mlir +// Always returns 4, can be constant folded: +%c0 = arith.constant 0 : index +%x = memref.dim %A, %c0 : memref<4 x ? x f32> + +// Returns the dynamic dimension of %A. +%c1 = arith.constant 1 : index +%y = memref.dim %A, %c1 : memref<4 x ? x f32> + +// Equivalent generic form: +%x = \"memref.dim\"(%A, %c0) : (memref<4 x ? x f32>, index) -> index +%y = \"memref.dim\"(%A, %c1) : (memref<4 x ? x f32>, index) -> index +``` +""" +function dim( + source::Value, index::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[source, index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "memref.dim", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`dma_start` + +# Syntax + +``` +operation ::= `memref.dma_start` ssa-use`[`ssa-use-list`]` `,` + ssa-use`[`ssa-use-list`]` `,` ssa-use `,` + ssa-use`[`ssa-use-list`]` (`,` ssa-use `,` ssa-use)? + `:` memref-type `,` memref-type `,` memref-type +``` + +DmaStartOp starts a non-blocking DMA operation that transfers data from a +source memref to a destination memref. The source and destination memref +need not be of the same dimensionality, but need to have the same elemental +type. The operands include the source and destination memref\'s each followed +by its indices, size of the data transfer in terms of the number of elements +(of the elemental type of the memref), a tag memref with its indices, and +optionally at the end, a stride and a number_of_elements_per_stride +arguments. The tag location is used by a DmaWaitOp to check for completion. +The indices of the source memref, destination memref, and the tag memref +have the same restrictions as any load/store. The optional stride arguments +should be of \'index\' type, and specify a stride for the slower memory space +(memory space with a lower memory space id), transferring chunks of +number_of_elements_per_stride every stride until %num_elements are +transferred. Either both or no stride arguments should be specified. If the +source and destination locations overlap the behavior of this operation is +not defined. + +For example, a DmaStartOp operation that transfers 256 elements of a memref +\'%src\' in memory space 0 at indices [%i, %j] to memref \'%dst\' in memory +space 1 at indices [%k, %l], would be specified as follows: + +```mlir +%num_elements = arith.constant 256 +%idx = arith.constant 0 : index +%tag = memref.alloc() : memref<1 x i32, affine_map<(d0) -> (d0)>, 4> +dma_start %src[%i, %j], %dst[%k, %l], %num_elements, %tag[%idx] : + memref<40 x 128 x f32>, affine_map<(d0) -> (d0)>, 0>, + memref<2 x 1024 x f32>, affine_map<(d0) -> (d0)>, 1>, + memref<1 x i32>, affine_map<(d0) -> (d0)>, 2> +``` + +If %stride and %num_elt_per_stride are specified, the DMA is expected to +transfer %num_elt_per_stride elements every %stride elements apart from +memory space 0 until %num_elements are transferred. + +```mlir +dma_start %src[%i, %j], %dst[%k, %l], %num_elements, %tag[%idx], %stride, + %num_elt_per_stride : +``` + +* TODO: add additional operands to allow source and destination striding, and +multiple stride levels. +* TODO: Consider replacing src/dst memref indices with view memrefs. +""" +function dma_start(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.dma_start", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`dma_wait` + +DmaWaitOp blocks until the completion of a DMA operation associated with the +tag element \'%tag[%index]\'. %tag is a memref, and %index has to be an index +with the same restrictions as any load/store index. %num_elements is the +number of elements associated with the DMA operation. + +# Example + +```mlir + dma_start %src[%i, %j], %dst[%k, %l], %num_elements, %tag[%index] : + memref<2048 x f32>, affine_map<(d0) -> (d0)>, 0>, + memref<256 x f32>, affine_map<(d0) -> (d0)>, 1> + memref<1 x i32>, affine_map<(d0) -> (d0)>, 2> + ... + ... + dma_wait %tag[%index], %num_elements : memref<1 x i32, affine_map<(d0) -> (d0)>, 2> + ``` +""" +function dma_wait( + tagMemRef::Value, tagIndices::Vector{Value}, numElements::Value; location=Location() +) + _results = IR.Type[] + _operands = Value[tagMemRef, tagIndices..., numElements] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.dma_wait", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`expand_shape` + +The `memref.expand_shape` op produces a new view with a higher rank whose +sizes are a reassociation of the original `view`. The operation is limited +to such reassociations, where a dimension is expanded into one or multiple +contiguous dimensions. Such reassociations never require additional allocs +or copies. + +A reassociation is defined as a grouping of dimensions and is represented +with an array of DenseI64ArrayAttr attributes. + +# Example + +```mlir +%r = memref.expand_shape %0 [[0, 1], [2]] output_shape [%sz0, %sz1, 32] + : memref into memref +``` + +If an op can be statically proven to be invalid (e.g, an expansion from +`memref<10xf32>` to `memref<2x6xf32>`), it is rejected by the verifier. If +it cannot statically be proven invalid (e.g., the full example above; it is +unclear whether the first source dimension is divisible by 5), the op is +accepted by the verifier. However, if the op is in fact invalid at runtime, +the behavior is undefined. + +The source memref can be zero-ranked. In that case, the reassociation +indices must be empty and the result shape may only consist of unit +dimensions. + +For simplicity, this op may not be used to cast dynamicity of dimension +sizes and/or strides. I.e., if and only if a source dimension is dynamic, +there must be a dynamic result dimension in the corresponding reassociation +group. Same for strides. + +The representation for the output shape supports a partially-static +specification via attributes specified through the `static_output_shape` +argument. A special sentinel value `ShapedType::kDynamic` encodes that the +corresponding entry has a dynamic value. There must be exactly as many SSA +inputs in `output_shape` as there are `ShapedType::kDynamic` entries in +`static_output_shape`. + +Note: This op currently assumes that the inner strides are of the +source/result layout map are the faster-varying ones. +""" +function expand_shape( + src::Value, + output_shape::Vector{Value}; + result::IR.Type, + reassociation, + static_output_shape, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[src, output_shape...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("reassociation", reassociation), + namedattribute("static_output_shape", static_output_shape), + ] + + return IR.create_operation( + "memref.expand_shape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`extract_aligned_pointer_as_index` + +Extracts the underlying aligned pointer as an index. + +This operation is useful for lowering to lower-level dialects while still +avoiding the need to define a pointer type in higher-level dialects such as +the memref dialect. + +This operation is intended solely as step during lowering, it has no side +effects. A reverse operation that creates a memref from an index interpreted +as a pointer is explicitly discouraged. + +# Example + +``` + %0 = memref.extract_aligned_pointer_as_index %arg : memref<4x4xf32> -> index + %1 = arith.index_cast %0 : index to i64 + %2 = llvm.inttoptr %1 : i64 to !llvm.ptr + call @foo(%2) : (!llvm.ptr) ->() +``` +""" +function extract_aligned_pointer_as_index( + source::Value; aligned_pointer=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(aligned_pointer) && push!(_results, aligned_pointer) + + return IR.create_operation( + "memref.extract_aligned_pointer_as_index", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`extract_strided_metadata` + +Extracts a base buffer, offset and strides. This op allows additional layers +of transformations and foldings to be added as lowering progresses from +higher-level dialect to lower-level dialects such as the LLVM dialect. + +The op requires a strided memref source operand. If the source operand is not +a strided memref, then verification fails. + +This operation is also useful for completeness to the existing memref.dim op. +While accessing strides, offsets and the base pointer independently is not +available, this is useful for composing with its natural complement op: +`memref.reinterpret_cast`. + +Intended Use Cases: + +The main use case is to expose the logic for manipulate memref metadata at a +higher level than the LLVM dialect. +This makes lowering more progressive and brings the following benefits: + - not all users of MLIR want to lower to LLVM and the information to e.g. + lower to library calls---like libxsmm---or to SPIR-V was not available. + - foldings and canonicalizations can happen at a higher level in MLIR: + before this op existed, lowering to LLVM would create large amounts of + LLVMIR. Even when LLVM does a good job at folding the low-level IR from + a performance perspective, it is unnecessarily opaque and inefficient to + send unkempt IR to LLVM. + +# Example + +```mlir + %base, %offset, %sizes:2, %strides:2 = + memref.extract_strided_metadata %memref : + memref<10x?xf32>, index, index, index, index, index + + // After folding, the type of %m2 can be memref<10x?xf32> and further + // folded to %memref. + %m2 = memref.reinterpret_cast %base to + offset: [%offset], + sizes: [%sizes#0, %sizes#1], + strides: [%strides#0, %strides#1] + : memref to memref +``` +""" +function extract_strided_metadata( + source::Value; + base_buffer=nothing::Union{Nothing,IR.Type}, + offset=nothing::Union{Nothing,IR.Type}, + sizes=nothing::Union{Nothing,Vector{IR.Type}}, + strides=nothing::Union{Nothing,Vector{IR.Type}}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(base_buffer) && push!(_results, base_buffer) + !isnothing(offset) && push!(_results, offset) + !isnothing(sizes) && push!(_results, sizes...) + !isnothing(strides) && push!(_results, strides...) + + return IR.create_operation( + "memref.extract_strided_metadata", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`get_global` + +The `memref.get_global` operation retrieves the memref pointing to a +named global variable. If the global variable is marked constant, writing +to the result memref (such as through a `memref.store` operation) is +undefined. + +# Example + +```mlir +%x = memref.get_global @foo : memref<2xf32> +``` +""" +function get_global(; result::IR.Type, name, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("name", name),] + + return IR.create_operation( + "memref.get_global", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`global_` + +The `memref.global` operation declares or defines a named global memref +variable. The backing memory for the variable is allocated statically and is +described by the type of the variable (which should be a statically shaped +memref type). The operation is a declaration if no `initial_value` is +specified, else it is a definition. The `initial_value` can either be a unit +attribute to represent a definition of an uninitialized global variable, or +an elements attribute to represent the definition of a global variable with +an initial value. The global variable can also be marked constant using the +`constant` unit attribute. Writing to such constant global variables is +undefined. + +The global variable can be accessed by using the `memref.get_global` to +retrieve the memref for the global variable. Note that the memref +for such global variable itself is immutable (i.e., memref.get_global for a +given global variable will always return the same memref descriptor). + +# Example + +```mlir +// Private variable with an initial value. +memref.global \"private\" @x : memref<2xf32> = dense<0.0,2.0> + +// Private variable with an initial value and an alignment (power of 2). +memref.global \"private\" @x : memref<2xf32> = dense<0.0,2.0> {alignment = 64} + +// Declaration of an external variable. +memref.global \"private\" @y : memref<4xi32> + +// Uninitialized externally visible variable. +memref.global @z : memref<3xf16> = uninitialized + +// Externally visible constant variable. +memref.global constant @c : memref<2xi32> = dense<1, 4> +``` +""" +function global_(; + sym_name, + sym_visibility=nothing, + type, + initial_value=nothing, + constant=nothing, + alignment=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("type", type) + ] + !isnothing(sym_visibility) && + push!(_attributes, namedattribute("sym_visibility", sym_visibility)) + !isnothing(initial_value) && + push!(_attributes, namedattribute("initial_value", initial_value)) + !isnothing(constant) && push!(_attributes, namedattribute("constant", constant)) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + + return IR.create_operation( + "memref.global", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`memory_space_cast` + +This operation casts memref values between memory spaces. +The input and result will be memrefs of the same types and shape that alias +the same underlying memory, though, for some casts on some targets, +the underlying values of the pointer stored in the memref may be affected +by the cast. + +The input and result must have the same shape, element type, rank, and layout. + +If the source and target address spaces are the same, this operation is a noop. + +# Example + +```mlir +// Cast a GPU private memory attribution into a generic pointer +%2 = memref.memory_space_cast %1 : memref to memref +// Cast a generic pointer to workgroup-local memory +%4 = memref.memory_space_cast %3 : memref<5x4xi32> to memref<5x34xi32, 3> +// Cast between two non-default memory spaces +%6 = memref.memory_space_cast %5 + : memref<*xmemref, 5> to memref<*xmemref, 3> +``` +""" +function memory_space_cast(source::Value; dest::IR.Type, location=Location()) + _results = IR.Type[dest,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.memory_space_cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`prefetch` + +The \"prefetch\" op prefetches data from a memref location described with +subscript indices similar to memref.load, and with three attributes: a +read/write specifier, a locality hint, and a cache type specifier as shown +below: + +```mlir +memref.prefetch %0[%i, %j], read, locality<3>, data : memref<400x400xi32> +``` + +The read/write specifier is either \'read\' or \'write\', the locality hint +ranges from locality<0> (no locality) to locality<3> (extremely local keep +in cache). The cache type specifier is either \'data\' or \'instr\' +and specifies whether the prefetch is performed on data cache or on +instruction cache. +""" +function prefetch( + memref::Value, + indices::Vector{Value}; + isWrite, + localityHint, + isDataCache, + location=Location(), +) + _results = IR.Type[] + _operands = Value[memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("isWrite", isWrite), + namedattribute("localityHint", localityHint), + namedattribute("isDataCache", isDataCache), + ] + + return IR.create_operation( + "memref.prefetch", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`rank` + +The `memref.rank` operation takes a memref operand and returns its rank. + +# Example + +```mlir +%0 = memref.rank %arg0 : memref<*xf32> +%1 = memref.rank %arg1 : memref +``` +""" +function rank(memref::Value; result_0=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[memref,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result_0) && push!(_results, result_0) + + return IR.create_operation( + "memref.rank", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`realloc` + +The `realloc` operation changes the size of a memory region. The memory +region is specified by a 1D source memref and the size of the new memory +region is specified by a 1D result memref type and an optional dynamic Value +of `Index` type. The source and the result memref must be in the same memory +space and have the same element type. + +The operation may move the memory region to a new location. In this case, +the content of the memory block is preserved up to the lesser of the new +and old sizes. If the new size if larger, the value of the extended memory +is undefined. This is consistent with the ISO C realloc. + +The operation returns an SSA value for the memref. + +# Example + +```mlir +%0 = memref.realloc %src : memref<64xf32> to memref<124xf32> +``` + +The source memref may have a dynamic shape, in which case, the compiler will +generate code to extract its size from the runtime data structure for the +memref. + +```mlir +%1 = memref.realloc %src : memref to memref<124xf32> +``` + +If the result memref has a dynamic shape, a result dimension operand is +needed to spefify its dynamic dimension. In the example below, the ssa value +\'%d\' specifies the unknown dimension of the result memref. + +```mlir +%2 = memref.realloc %src(%d) : memref to memref +``` + +An optional `alignment` attribute may be specified to ensure that the +region of memory that will be indexed is aligned at the specified byte +boundary. This is consistent with the fact that memref.alloc supports such +an optional alignment attribute. Note that in ISO C standard, neither alloc +nor realloc supports alignment, though there is aligned_alloc but not +aligned_realloc. + +```mlir +%3 = memref.realloc %src {alignment = 8} : memref<64xf32> to memref<124xf32> +``` + +Referencing the memref through the old SSA value after realloc is undefined +behavior. + +```mlir +%new = memref.realloc %old : memref<64xf32> to memref<124xf32> +%4 = memref.load %new[%index] // ok +%5 = memref.load %old[%index] // undefined behavior +``` +""" +function realloc( + source::Value, + dynamicResultSize=nothing::Union{Nothing,Value}; + result_0::IR.Type, + alignment=nothing, + location=Location(), +) + _results = IR.Type[result_0,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(dynamicResultSize) && push!(_operands, dynamicResultSize) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + + return IR.create_operation( + "memref.realloc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reinterpret_cast` + +Modify offset, sizes and strides of an unranked/ranked memref. + +# Example +```mlir +memref.reinterpret_cast %ranked to + offset: [0], + sizes: [%size0, 10], + strides: [1, %stride1] +: memref to memref> + +memref.reinterpret_cast %unranked to + offset: [%offset], + sizes: [%size0, %size1], + strides: [%stride0, %stride1] +: memref<*xf32> to memref> +``` + +This operation creates a new memref descriptor using the base of the +source and applying the input arguments to the other metadata. +In other words: +```mlir +%dst = memref.reinterpret_cast %src to + offset: [%offset], + sizes: [%sizes], + strides: [%strides] +``` +means that `%dst`\'s descriptor will be: +```mlir +%dst.base = %src.base +%dst.aligned = %src.aligned +%dst.offset = %offset +%dst.sizes = %sizes +%dst.strides = %strides +``` +""" +function reinterpret_cast( + source::Value, + offsets::Vector{Value}, + sizes::Vector{Value}, + strides::Vector{Value}; + result::IR.Type, + static_offsets, + static_sizes, + static_strides, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[source, offsets..., sizes..., strides...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("static_offsets", static_offsets), + namedattribute("static_sizes", static_sizes), + namedattribute("static_strides", static_strides), + ] + push!( + _attributes, + operandsegmentsizes([1, length(offsets), length(sizes), length(strides)]), + ) + + return IR.create_operation( + "memref.reinterpret_cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reshape` + +The `reshape` operation converts a memref from one type to an +equivalent type with a provided shape. The data is never copied or +modified. The source and destination types are compatible if both have the +same element type, same number of elements, address space and identity +layout map. The following combinations are possible: + +a. Source type is ranked or unranked. Shape argument has static size. +Result type is ranked. + +```mlir +// Reshape statically-shaped memref. +%dst = memref.reshape %src(%shape) + : (memref<4x1xf32>, memref<1xi32>) to memref<4xf32> +%dst0 = memref.reshape %src(%shape0) + : (memref<4x1xf32>, memref<2xi32>) to memref<2x2xf32> +// Flatten unranked memref. +%dst = memref.reshape %src(%shape) + : (memref<*xf32>, memref<1xi32>) to memref +``` + +b. Source type is ranked or unranked. Shape argument has dynamic size. +Result type is unranked. + +```mlir +// Reshape dynamically-shaped 1D memref. +%dst = memref.reshape %src(%shape) + : (memref, memref) to memref<*xf32> +// Reshape unranked memref. +%dst = memref.reshape %src(%shape) + : (memref<*xf32>, memref) to memref<*xf32> +``` +""" +function reshape(source::Value, shape::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[source, shape] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.reshape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`store` + +Store a value to a memref location given by indices. The value stored should +have the same type as the elemental type of the memref. The number of +arguments provided within brackets need to match the rank of the memref. + +In an affine context, the indices of a store are restricted to SSA values +bound to surrounding loop induction variables, +[symbols](Affine.md/#restrictions-on-dimensions-and-symbols), results of a +`constant` operation, or the result of an +[`affine.apply`](Affine.md/#affineapply-affineapplyop) operation that can in +turn take as arguments all of the aforementioned SSA values or the +recursively result of such an `affine.apply` operation. + +# Example + +```mlir +memref.store %100, %A[%1, 1023] : memref<4x?xf32, #layout, memspace0> +``` + +**Context:** The `load` and `store` operations are specifically crafted to +fully resolve a reference to an element of a memref, and (in polyhedral +`affine.if` and `affine.for` operations) the compiler can follow use-def +chains (e.g. through [`affine.apply`](Affine.md/#affineapply-affineapplyop) +operations) to precisely analyze references at compile-time using polyhedral +techniques. This is possible because of the +[restrictions on dimensions and symbols](Affine.md/#restrictions-on-dimensions-and-symbols) +in these contexts. +""" +function store( + value::Value, + memref::Value, + indices::Vector{Value}; + nontemporal=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, memref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(nontemporal) && + push!(_attributes, namedattribute("nontemporal", nontemporal)) + + return IR.create_operation( + "memref.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`transpose` + +The `transpose` op produces a strided memref whose sizes and strides +are a permutation of the original `in` memref. This is purely a metadata +transformation. + +# Example + +```mlir +%1 = memref.transpose %0 (i, j) -> (j, i) : memref to memref (d1 * s0 + d0)>> +``` +""" +function transpose(in::Value; result_0::IR.Type, permutation, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[in,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("permutation", permutation),] + + return IR.create_operation( + "memref.transpose", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`view` + +The \"view\" operation extracts an N-D contiguous memref with empty layout map +with arbitrary element type from a 1-D contiguous memref with empty layout +map of i8 element type. The ViewOp supports the following arguments: + +* A single dynamic byte-shift operand must be specified which represents a + a shift of the base 1-D memref pointer from which to create the resulting + contiguous memref view with identity layout. +* A dynamic size operand that must be specified for each dynamic dimension + in the resulting view memref type. + +The \"view\" operation gives a structured indexing form to a flat 1-D buffer. +Unlike \"subview\" it can perform a type change. The type change behavior +requires the op to have special semantics because, e.g. a byte shift of 3 +cannot be represented as an offset on f64. +For now, a \"view\" op: + +1. Only takes a contiguous source memref with 0 offset and empty layout. +2. Must specify a byte_shift operand (in the future, a special integer + attribute may be added to support the folded case). +3. Returns a contiguous memref with 0 offset and empty layout. + +# Example + +```mlir +// Allocate a flat 1D/i8 memref. +%0 = memref.alloc() : memref<2048xi8> + +// ViewOp with dynamic offset and static sizes. +%1 = memref.view %0[%offset_1024][] : memref<2048xi8> to memref<64x4xf32> + +// ViewOp with dynamic offset and two dynamic size. +%2 = memref.view %0[%offset_1024][%size0, %size1] : + memref<2048xi8> to memref +``` +""" +function view( + source::Value, + byte_shift::Value, + sizes::Vector{Value}; + result_0::IR.Type, + location=Location(), +) + _results = IR.Type[result_0,] + _operands = Value[source, byte_shift, sizes...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "memref.view", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`subview` + +The \"subview\" operation converts a memref type to another memref type +which represents a reduced-size view of the original memref as specified by +the operation\'s offsets, sizes and strides arguments. + +The SubView operation supports the following arguments: + +* source: the \"base\" memref on which to create a \"view\" memref. +* offsets: memref-rank number of offsets into the \"base\" memref at which to + create the \"view\" memref. +* sizes: memref-rank number of sizes which specify the sizes of the result + \"view\" memref type. +* strides: memref-rank number of strides that compose multiplicatively with + the base memref strides in each dimension. + +The representation based on offsets, sizes and strides support a +partially-static specification via attributes specified through the +`static_offsets`, `static_sizes` and `static_strides` arguments. A special +sentinel value ShapedType::kDynamic encodes that the corresponding entry has +a dynamic value. + +A subview operation may additionally reduce the rank of the resulting view +by removing dimensions that are statically known to be of size 1. + +Example 1: + +```mlir +%0 = memref.alloc() : memref<64x4xf32, affine_map<(d0, d1) -> (d0 * 4 + d1)>> + +// Create a sub-view of \"base\" memref \'%0\' with offset arguments \'%c0\', +// dynamic sizes for each dimension, and stride arguments \'%c1\'. +%1 = memref.subview %0[%c0, %c0][%size0, %size1][%c1, %c1] + : memref<64x4xf32, affine_map<(d0, d1) -> (d0 * 4 + d1)>> to + memref (d0 * s1 + d1 + s0)>> +``` + +Example 2: + +```mlir +%0 = memref.alloc() : memref<8x16x4xf32, affine_map<(d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)>> + +// Create a sub-view of \"base\" memref \'%0\' with dynamic offsets, sizes, +// and strides. +// Note that dynamic offsets are represented by the linearized dynamic +// offset symbol \'s0\' in the subview memref layout map, and that the +// dynamic strides operands, after being applied to the base memref +// strides in each dimension, are represented in the view memref layout +// map as symbols \'s1\', \'s2\' and \'s3\'. +%1 = memref.subview %0[%i, %j, %k][%size0, %size1, %size2][%x, %y, %z] + : memref<8x16x4xf32, affine_map<(d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)>> to + memref (d0 * s1 + d1 * s2 + d2 * s3 + s0)>> +``` + +Example 3: + +```mlir +%0 = memref.alloc() : memref<8x16x4xf32, affine_map<(d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)>> + +// Subview with constant offsets, sizes and strides. +%1 = memref.subview %0[0, 2, 0][4, 4, 4][1, 1, 1] + : memref<8x16x4xf32, affine_map<(d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2)>> to + memref<4x4x4xf32, affine_map<(d0, d1, d2) -> (d0 * 64 + d1 * 4 + d2 + 8)>> +``` + +Example 4: + +```mlir +%0 = memref.alloc(%arg0, %arg1) : memref + +// Subview with constant size, but dynamic offsets and +// strides. The resulting memref has a static shape, but if the +// base memref has an affine map to describe the layout, the result +// memref also uses an affine map to describe the layout. The +// strides of the result memref is computed as follows: +// +// Let #map1 represents the layout of the base memref, and #map2 +// represents the layout of the result memref. A #mapsubview can be +// constructed to map an index from the result memref to the base +// memref (note that the description below uses more convenient +// naming for symbols, while in affine maps, symbols are +// represented as unsigned numbers that identify that symbol in the +// given affine map. +// +// #mapsubview = (d0, d1)[o0, o1, t0, t1] -> (d0 * t0 + o0, d1 * t1 + o1) +// +// where, o0, o1, ... are offsets, and t0, t1, ... are strides. Then, +// +// #map2 = #map1.compose(#mapsubview) +// +// If the layout map is represented as +// +// #map1 = (d0, d1)[s0, s1, s2] -> (d0 * s1 + d1 * s2 + s0) +// +// then, +// +// #map2 = (d0, d1)[s0, s1, s2, o0, o1, t0, t1] -> +// (d0 * s1 * t0 + d1 * s2 * t1 + o0 * s1 + o1 * s2 + s0) +// +// Representing this canonically +// +// #map2 = (d0, d1)[r0, r1, r2] -> (d0 * r1 + d1 * r2 + r0) +// +// where, r0 = o0 * s1 + o1 * s2 + s0, r1 = s1 * t0, r2 = s2 * t1. +%1 = memref.subview %0[%i, %j][4, 4][%x, %y] : + : memref (d0 * s1 + d1 * s2 + s0)>> to + memref<4x4xf32, affine_map<(d0, d1)[r0, r1, r2] -> (d0 * r1 + d1 * r2 + r0)>> + +// Note that the subview op does not guarantee that the result +// memref is \"inbounds\" w.r.t to base memref. It is upto the client +// to ensure that the subview is accessed in a manner that is +// in-bounds. +``` + +Example 5: + +```mlir +// Rank-reducing subview. +%1 = memref.subview %0[0, 0, 0][1, 16, 4][1, 1, 1] : + memref<8x16x4xf32> to memref<16x4xf32> + +// Original layout: +// (d0, d1, d2) -> (64 * d0 + 16 * d1 + d2) +// Subviewed layout: +// (d0, d1, d2) -> (64 * (d0 + 3) + 4 * (d1 + 4) + d2 + 2) = (64 * d0 + 4 * d1 + d2 + 210) +// After rank reducing: +// (d0, d1) -> (4 * d0 + d1 + 210) +%3 = memref.subview %2[3, 4, 2][1, 6, 3][1, 1, 1] : + memref<8x16x4xf32> to memref<6x3xf32, strided<[4, 1], offset: 210>> +``` +""" +function subview( + source::Value, + offsets::Vector{Value}, + sizes::Vector{Value}, + strides::Vector{Value}; + result::IR.Type, + static_offsets, + static_sizes, + static_strides, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[source, offsets..., sizes..., strides...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("static_offsets", static_offsets), + namedattribute("static_sizes", static_sizes), + namedattribute("static_strides", static_strides), + ] + push!( + _attributes, + operandsegmentsizes([1, length(offsets), length(sizes), length(strides)]), + ) + + return IR.create_operation( + "memref.subview", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # memref diff --git a/src/Dialects/19/Mesh.jl b/src/Dialects/19/Mesh.jl new file mode 100644 index 00000000..c3c8777f --- /dev/null +++ b/src/Dialects/19/Mesh.jl @@ -0,0 +1,977 @@ +module mesh + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`all_gather` + +Gathers along the `gather_axis` tensor axis. + +# Example +```mlir +mesh.mesh @mesh0(shape = 2x2) +... +%1 = mesh.all_gather %0 on @mesh0 mesh_axes = [1] gather_axis = 1 + : tensor<2x2xi8> -> tensor<2x4xi8> +``` +Input: +``` + +-------+-------+ +device (0, 0) -> | 1 2 | 5 6 | <- device (0, 1) + | 3 4 | 7 8 | + +-------+-------+ +device (1, 0) -> | 9 10 | 13 14 | <- device (1, 1) + | 11 12 | 15 16 | + +-------+-------+ +``` +Result: +``` +gather tensor +axis 1 +------------> ++-------------+ +| 1 2 5 6 | <- devices (0, 0) and (0, 1) +| 3 4 7 8 | ++-------------+ +| 9 10 13 14 | <- devices (1, 0) and (1, 1) +| 11 12 15 16 | ++-------------+ +``` +""" +function all_gather( + input::Value; result::IR.Type, mesh, mesh_axes=nothing, gather_axis, location=Location() +) + _results = IR.Type[result,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("mesh", mesh), namedattribute("gather_axis", gather_axis) + ] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + + return IR.create_operation( + "mesh.all_gather", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`all_reduce` + +The accumulation element type is specified by the result type and +it does not need to match the input element type. +The input element is converted to the result element type before +performing the reduction. + +Attributes: +`reduction`: Indicates the reduction method. + +# Example +``` +%1 = mesh.all_reduce %0 on @mesh0 mesh_axes = [1, 0] reduction = + : tensor<3x4xf32> -> tensor<3x4xf64> +``` +""" +function all_reduce( + input::Value; + result::IR.Type, + mesh, + mesh_axes=nothing, + reduction=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mesh", mesh),] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + !isnothing(reduction) && push!(_attributes, namedattribute("reduction", reduction)) + + return IR.create_operation( + "mesh.all_reduce", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`all_slice` + +Slice along the `slice_axis` tensor axis. +This operation can be thought of as the inverse of all-gather. +Technically, it is not required that all processes have the same input tensor. +Each process will slice a piece of its local tensor based on its in-group device index. +The operation does not communicate data between devices. + +# Example +```mlir +mesh.mesh @mesh0(shape = 2x2) +... +%1 = mesh.all_slice %0 on @mesh0 mesh_axes = [1] slice_axis = 1 + : tensor<2x4xi8> -> tensor<2x2xi8> +``` +Input: +``` ++-------------+ +| 1 2 5 6 | <- devices (0, 0) and (0, 1) +| 3 4 7 8 | ++-------------+ +| 9 10 13 14 | <- devices (1, 0) and (1, 1) +| 11 12 15 16 | ++-------------+ +``` +Result: +``` +gather tensor +axis 1 +------------> + +-------+-------+ +device (0, 0) -> | 1 2 | 5 6 | <- device (0, 1) + | 3 4 | 7 8 | + +-------+-------+ +device (1, 0) -> | 9 10 | 13 14 | <- device (1, 1) + | 11 12 | 15 16 | + +-------+-------+ +``` +""" +function all_slice( + input::Value; result::IR.Type, mesh, mesh_axes=nothing, slice_axis, location=Location() +) + _results = IR.Type[result,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("mesh", mesh), namedattribute("slice_axis", slice_axis) + ] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + + return IR.create_operation( + "mesh.all_slice", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`all_to_all` + +Performs an all-to-all on tensor pieces split along `split_axis`. +The resulting pieces are concatenated along `concat_axis` on ech device. + +# Example +``` +mesh.mesh @mesh0(shape = 3) +... +%1 = mesh.all_to_all %0 on @mesh0 mesh_axes = [0] + split_axis = 0 concat_axis = 0 + : tensor<3x2xi8> -> tensor<3x2xi8> +``` +Input: +``` + device device device + (0) (1) (2) ++-------+-------+-------+ | split and concat along +| 11 12 | 21 22 | 31 32 | | tensor axis 0 +| 13 14 | 23 24 | 33 34 | ↓ +| 15 16 | 25 26 | 35 36 | ++-------+-------+-------+ +``` +Result: +``` + device device device + (0) (1) (2) ++-------+-------+-------+ +| 11 12 | 13 14 | 15 16 | +| 21 22 | 23 24 | 25 26 | +| 31 32 | 33 34 | 35 36 | ++-------+-------+-------+ +``` +""" +function all_to_all( + input::Value; + result::IR.Type, + mesh, + mesh_axes=nothing, + split_axis, + concat_axis, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("mesh", mesh), + namedattribute("split_axis", split_axis), + namedattribute("concat_axis", concat_axis), + ] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + + return IR.create_operation( + "mesh.all_to_all", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`broadcast` + +Broadcast the tensor on `root` to all devices in each respective group. +The operation broadcasts along mesh axes `mesh_axes`. +The `root` device specifies the in-group multi-index that is broadcast to +all other devices in the group. + +# Example +``` +mesh.mesh @mesh0(shape = 2x2) + +%1 = mesh.broadcast %0 on @mesh0 + mesh_axes = [0] + root = [0] + : (tensor<2xi8>) -> tensor<2xi8> +``` + +Input: +``` + +-------+-------+ | broadcast +device (0, 0) -> | 1 2 | 3 4 | <- device (0, 1) | along axis 0 + +-------+-------+ ↓ +device (1, 0) -> | | | <- device (1, 1) + +-------+-------+ +``` + +Output: +``` + +-------+-------+ +device (0, 0) -> | 1 2 | 3 4 | <- device (0, 1) + +-------+-------+ +device (1, 0) -> | 1 2 | 3 4 | <- device (1, 1) + +-------+-------+ +``` +""" +function broadcast( + input::Value, + root_dynamic::Vector{Value}; + result::IR.Type, + mesh, + mesh_axes=nothing, + root, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[input, root_dynamic...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mesh", mesh), namedattribute("root", root)] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + + return IR.create_operation( + "mesh.broadcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`gather` + +Gathers on device `root` along the `gather_axis` tensor axis. +`root` specifies the coordinates of a device along `mesh_axes`. +It uniquely identifies the root device for each device group. +The result tensor on non-root devices is undefined. +Using it will result in undefined behavior. + +# Example +```mlir +mesh.mesh @mesh0(shape = 2x2) +... +%1 = mesh.gather %0 on @mesh0 mesh_axes = [1] + gather_axis = 1 root = [1] + : (tensor<2x2xi8>) -> tensor<2x4xi8> +``` +Input: +``` + gather tensor + axis 1 + ------------> + +-------+-------+ +device (0, 0) -> | 1 2 | 5 6 | <- device (0, 1) + | 3 4 | 7 8 | + +-------+-------+ +device (1, 0) -> | 9 10 | 13 14 | <- device (1, 1) + | 11 12 | 15 16 | + +-------+-------+ +``` +Result: +``` ++-------------+ +| 1 2 5 6 | <- devices (0, 1) +| 3 4 7 8 | ++-------------+ +| 9 10 13 14 | <- devices (1, 1) +| 11 12 15 16 | ++-------------+ +``` +Devices `(0, 0)` and `(1, 0)` have undefined result. +""" +function gather( + input::Value, + root_dynamic::Vector{Value}; + result::IR.Type, + mesh, + mesh_axes=nothing, + gather_axis, + root, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[input, root_dynamic...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("mesh", mesh), + namedattribute("gather_axis", gather_axis), + namedattribute("root", root), + ] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + + return IR.create_operation( + "mesh.gather", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mesh_` + +The mesh.mesh operation is a symbol operation that identifies a specific +mesh. The operation has three attributes: + +1. `sym_name`: This attribute uniquely identifies the name of the mesh. +This name serves as a symbolic reference to the mesh throughout +the MLIR module, allowing for consistent referencing and easier debugging. + +2. `shape`: This attribute represents the shape of the device mesh. +It uses the same notation as a tensor shape. Also allowing for dynamic +dimensions. +This flexibility allows for dynamic device assignment or configurations +where the exact number of devices might not be determined during compile +time. +For example `2x?x4`. + +# Example +``` +// A device mesh with 3 axes, the total device number is 4 * 8 * 12 +// The dimension sizes are 4, 8, 12 +mesh.mesh @mesh0(shape = 4x8x12) + +// A device mesh with 2 axes, the total device number is unknown +// The first dimension size is 4 and the second is unknown +mesh.mesh @mesh1(shape = 4x?) + +// A device mesh with 2 axes, the total device number is unknown +// The first dimension size is unknown and the second is 4 +mesh.mesh @mesh2(shape = ?x4) + +// A device mesh with 2 axes, the number of devices along both axes +// is unknown +mesh.mesh @mesh3(shape = ?x?) +``` +""" +function mesh_(; sym_name, shape, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("shape", shape) + ] + + return IR.create_operation( + "mesh.mesh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mesh_shape` + +""" +function mesh_shape(; result::Vector{IR.Type}, mesh, axes=nothing, location=Location()) + _results = IR.Type[result...,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mesh", mesh),] + !isnothing(axes) && push!(_attributes, namedattribute("axes", axes)) + + return IR.create_operation( + "mesh.mesh_shape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`process_linear_index` + +# Example +``` +%idx = mesh.process_linear_index on @mesh : index +``` +if `@mesh` has shape `(10, 20, 30)`, a device with multi +index `(1, 2, 3)` will have linear index `3 + 30*2 + 20*30*1`. +""" +function process_linear_index(; + result=nothing::Union{Nothing,IR.Type}, mesh, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mesh", mesh),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "mesh.process_linear_index", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`process_multi_index` + +It is used in the SPMD format of IR. +The `axes` mush be non-negative and less than the total number of mesh axes. +If the axes are empty then get the index along all axes. +""" +function process_multi_index(; + result::Vector{IR.Type}, mesh, axes=nothing, location=Location() +) + _results = IR.Type[result...,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mesh", mesh),] + !isnothing(axes) && push!(_attributes, namedattribute("axes", axes)) + + return IR.create_operation( + "mesh.process_multi_index", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`recv` + +Receive from a device within a device group. +""" +function recv( + input::Value, + source_dynamic::Vector{Value}; + result::IR.Type, + mesh, + mesh_axes=nothing, + source=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[input, source_dynamic...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mesh", mesh),] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + !isnothing(source) && push!(_attributes, namedattribute("source", source)) + + return IR.create_operation( + "mesh.recv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reduce` + +Reduces on device `root` within each device group. +`root` specifies the coordinates of a device along `mesh_axes`. +It uniquely identifies the root device within its device group. +The accumulation element type is specified by the result type and +it does not need to match the input element type. +The input element is converted to the result element type before +performing the reduction. + +Attributes: +`reduction`: Indicates the reduction method. + +# Example +``` +%1 = mesh.reduce %0 on @mesh0 mesh_axes = [1, 0] + reduction = root = [2, 3] + : (tensor<3x4xf32>) -> tensor<3x4xf64> +``` +""" +function reduce( + input::Value, + root_dynamic::Vector{Value}; + result::IR.Type, + mesh, + mesh_axes=nothing, + reduction=nothing, + root, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[input, root_dynamic...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mesh", mesh), namedattribute("root", root)] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + !isnothing(reduction) && push!(_attributes, namedattribute("reduction", reduction)) + + return IR.create_operation( + "mesh.reduce", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reduce_scatter` + +After the reduction, the result is scattered within each device group. +The tensor is split along `scatter_axis` and the pieces distributed +across the device group. +# Example +``` +mesh.mesh @mesh0(shape = 2x2) +... +%1 = mesh.reduce_scatter %0 on @mesh0 mesh_axes = [1] + reduction = scatter_axis = 0 + : tensor<3x4xf32> -> tensor<1x4xf64> +``` +Input: +``` + device + (0, 1) + ↓ + +-------+-------+ | scatter tensor +device (0, 0) -> | 1 2 | 5 6 | | axis 0 + | 3 4 | 7 8 | ↓ + +-------+-------+ +device (1, 0) -> | 9 10 | 13 14 | + | 11 12 | 15 16 | + +-------+-------+ + ↑ + device + (1, 1) +``` +Result: +``` ++-------+ +| 6 8 | <- devices (0, 0) ++-------+ +| 10 12 | <- devices (0, 1) ++-------+ +| 22 24 | <- devices (1, 0) ++-------+ +| 26 28 | <- devices (1, 1) ++-------+ +``` +""" +function reduce_scatter( + input::Value; + result::IR.Type, + mesh, + mesh_axes=nothing, + reduction=nothing, + scatter_axis, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("mesh", mesh), namedattribute("scatter_axis", scatter_axis) + ] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + !isnothing(reduction) && push!(_attributes, namedattribute("reduction", reduction)) + + return IR.create_operation( + "mesh.reduce_scatter", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`scatter` + +For each device group split the input tensor on the `root` device along +axis `scatter_axis` and scatter the parts across the group devices. + +# Example +``` +mesh.mesh @mesh0(shape = 2x2) +%1 = mesh.scatter %0 on @mesh0 mesh_axes = [0] + scatter_axis = 0 + root = [1] + : (tensor<2x2xi8>) -> tensor<1x2xi8> +``` + +Input: +``` + device + (0, 1) + ↓ + +-------+-------+ | scatter tensor +device (0, 0) -> | | | | axis 0 + | | | ↓ + +-------+-------+ +device (1, 0) -> | 1 2 | 5 6 | + | 3 4 | 7 8 | + +-------+-------+ + ↑ + device + (1, 1) +``` + +Result: +``` + device + (0, 1) + ↓ + +-------+-------+ +device (0, 0) -> | 1 2 | 5 6 | + +-------+-------+ +device (1, 0) -> | 3 4 | 7 8 | + +-------+-------+ + ↑ + device + (1, 1) +``` +""" +function scatter( + input::Value, + root_dynamic::Vector{Value}; + result::IR.Type, + mesh, + mesh_axes=nothing, + scatter_axis, + root, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[input, root_dynamic...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("mesh", mesh), + namedattribute("scatter_axis", scatter_axis), + namedattribute("root", root), + ] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + + return IR.create_operation( + "mesh.scatter", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`send` + +Send from one device to another within a device group. +""" +function send( + input::Value, + destination_dynamic::Vector{Value}; + result::IR.Type, + mesh, + mesh_axes=nothing, + destination, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[input, destination_dynamic...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("mesh", mesh), namedattribute("destination", destination) + ] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + + return IR.create_operation( + "mesh.send", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`shard` + +The mesh.shard operation is designed to specify and guide the sharding +behavior of a tensor value across a mesh topology. This operation has one +operand and two attributes: + +1. `input`: This operand represents the tensor value that needs to be +annotated for sharding. + +2. `shard`: This attribute is type of `MeshSharding`, which is the core data +structure to represent distribution of a tensor on a mesh. + +3. `annotate_for_users`: A unit attribute addressing the scenario when a +tensor\'s sharding annotation differs based on its context of use (either as +a result or an operand). If specified, the sharding pertains to specific +users of the tensor value, indicating how it should be considered when used +as an operand in subsequent operations. If not, the sharding applies to the +operation that defines the tensor value. + +# Example +``` +func.func @only_result_annotated(%arg0 : tensor<4x8xf32>) -> () { + %0 = mesh.shard %arg0 to <@mesh0, [[0]]> : tensor<4x8xf32> + ... +} + +func.func @only_operand_annotated(%arg0 : tensor<4x8xf32>) -> () { + %0 = mesh.shard %arg0 to <@mesh0, [[0]]> annotate_for_users : tensor<4x8xf32> + ... +} + +// The first mesh.shard op applies to %arg0, the second mesh.shard op +// applies for the operand of op0, the third mesh.shard op applies for the +// operand of op2 +func.func @both_result_and_multi_operands_annotated( + %arg0 : tensor<4x8xf32>) -> () { + %0 = mesh.shard %arg0 to <@mesh0, [[0]]> : tensor<4x8xf32> + %1 = mesh.shard %0 to <@mesh0, [[1]]> annotate_for_users : tensor<4x8xf32> + %2 = mesh.shard %0 to <@mesh0, [[2]]> annotate_for_users : tensor<4x8xf32> + \"op0\"(%1) : ... + \"op1\"(%2) : ... + ... +} +``` + +The following usages are undefined: +``` +func.func @annotate_on_same_result_with_different_sharding( + %arg0 : tensor<4x8xf32>) -> () { + %0 = mesh.shard %arg0 to <@mesh0, [[0]]> : tensor<4x8xf32> + %1 = mesh.shard %0 to <@mesh0, [[1]]> : tensor<4x8xf32> + ... +} + +func.func @annotate_on_same_result_same_value_with_different_sharding( + %arg0 : tensor<4x8xf32>) -> () { + %0 = mesh.shard %arg0 to <@mesh0, [[0]]> : tensor<4x8xf32> + %1 = mesh.shard %arg0 to <@mesh0, [[1]]> : tensor<4x8xf32> + ... +} + +func.func @annotate_on_same_operand_with_different_sharding( + %arg0 : tensor<4x8xf32>) -> () { + %0 = mesh.shard %arg0 to <@mesh0, [[0]]> annotate_for_users : tensor<4x8xf32> + %1 = mesh.shard %0 to <@mesh0, [[1]]> annotate_for_users : tensor<4x8xf32> + ... +} + +func.func @result_annotated_after_operand( + %arg0 : tensor<4x8xf32>) -> () { + %0 = mesh.shard %arg0 to <@mesh0, [[0]]> annotate_for_users : tensor<4x8xf32> + %1 = mesh.shard %0 to <@mesh0, [[1]]> : tensor<4x8xf32> + ... +} +``` +""" +function shard( + src::Value; + result=nothing::Union{Nothing,IR.Type}, + shard, + annotate_for_users=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[src,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("shard", shard),] + !isnothing(result) && push!(_results, result) + !isnothing(annotate_for_users) && + push!(_attributes, namedattribute("annotate_for_users", annotate_for_users)) + + return IR.create_operation( + "mesh.shard", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`shift` + +Within each device group shift along mesh axis `shift_axis` by an offset +`offset`. +The result on devices that do not have a corresponding source is undefined. +`shift_axis` must be one of `mesh_axes`. +If the `rotate` attribute is present, +instead of a shift a rotation is done. + +# Example +``` +mesh.mesh @mesh0(shape = 2x4) +%1 = mesh.shift on @mesh0 mesh_axes = [1] + shift_axis = 1 offset = 2 rotate + : tensor<2xi8> -> tensor<2xi8> +``` + +Input: +``` +mesh axis 1 +-----------> + ++----+----+----+----+ +| 1 | 2 | 3 | 4 | ++----+----+----+----+ +| 5 | 6 | 7 | 8 | ++----+----+----+----+ +``` + +Result: +``` ++----+----+----+----+ +| 3 | 4 | 1 | 2 | ++----+----+----+----+ +| 7 | 8 | 5 | 6 | ++----+----+----+----+ +``` +""" +function shift( + input::Value; + result::IR.Type, + mesh, + mesh_axes=nothing, + shift_axis, + offset, + rotate=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("mesh", mesh), + namedattribute("shift_axis", shift_axis), + namedattribute("offset", offset), + ] + !isnothing(mesh_axes) && push!(_attributes, namedattribute("mesh_axes", mesh_axes)) + !isnothing(rotate) && push!(_attributes, namedattribute("rotate", rotate)) + + return IR.create_operation( + "mesh.shift", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # mesh diff --git a/src/Dialects/19/NVGPU.jl b/src/Dialects/19/NVGPU.jl new file mode 100644 index 00000000..26af2403 --- /dev/null +++ b/src/Dialects/19/NVGPU.jl @@ -0,0 +1,881 @@ +module nvgpu + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`device_async_copy` + +The `nvgpu.device_async_copy` op initiates an asynchronous copy operation of +elements from source (global memory) to the destination (shared memory) +without blocking the thread. The async copy is added to a group. + +This op is meant to be used with `nvgpu.device_async_create_group` and +`nvgpu.device_async_wait` to synchronize copies as explained in those ops +descriptions. + +`bypassL1` attribute is hint to the hardware to bypass the L1 cache during +async copy, this hint may be ignored by the hardware. + +`dstElements` attribute is the total number of elements written to +destination (shared memory). + +`srcElements` argument is the total number of elements read from +source (global memory). + +`srcElements` is an optional argument and when present the op only reads +`srcElements` number of elements from the source (global memory) and zero fills +the rest of the elements in the destination (shared memory). + +In order to do a copy and wait for the result we need the following +combination: +``` +// copy 1. +%cp1 = nvgpu.device_async_copy %A[%c0], %B[%c0], 4 :memref<16xf32> to memref<16xf32, 3> +// copy 2. +%cp2 = nvgpu.device_async_copy %C[%c0], %D[%c0], 4 : memref<16xf32> to memref<16xf32, 3> +// group 1 contains copy 1 and copy 2. +%token1 = nvgpu.device_async_create_group %cp1, %cp2 +// copy 3. +%cp3 = nvgpu.device_async_copy %E[%c0], %F[%c0], 4 : memref<16xf32> to memref<16xf32, 3> +// group 2 contains copy 3. +%token2 = nvgpu.device_async_create_group %cp3 +// after the wait copy 1 and copy 2 are complete. +nvgpu.device_async_wait %token1 +// after the wait copy 3 is complete. +nvgpu.device_async_wait %token2 +``` + +# Example + +```mlir +%0 = nvgpu.device_async_copy %src[%c0, %c0], %dst[%c0, %c0, %c0], 4 : + memref<4x5xf32> to memref<2x7x5xf32, 3> +``` +""" +function device_async_copy( + dst::Value, + dstIndices::Vector{Value}, + src::Value, + srcIndices::Vector{Value}, + srcElements=nothing::Union{Nothing,Value}; + asyncToken::IR.Type, + dstElements, + bypassL1=nothing, + location=Location(), +) + _results = IR.Type[asyncToken,] + _operands = Value[dst, dstIndices..., src, srcIndices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dstElements", dstElements),] + !isnothing(srcElements) && push!(_operands, srcElements) + push!( + _attributes, + operandsegmentsizes([ + 1, length(dstIndices), 1, length(srcIndices), isnothing(srcElements) ? 0 : 1 + ]), + ) + !isnothing(bypassL1) && push!(_attributes, namedattribute("bypassL1", bypassL1)) + + return IR.create_operation( + "nvgpu.device_async_copy", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`device_async_create_group` + +The `nvgpu.device_async_create_group` op creates a group of memory accesses +containing all the pending `device_async_copy` operations associated with +argument tokens. Each token can only be part of one group. + +It returns a token that can be use to wait until the group fully completes. + +This is meant to be used with `nvgpu.device_async_wait` to synchronize copies +as explained in those ops descriptions. + +Groups are executed in the order they are created. + +# Example + +```mlir +%0 = nvgpu.device_async_create_group + ``` +""" +function device_async_create_group( + inputTokens::Vector{Value}; asyncToken::IR.Type, location=Location() +) + _results = IR.Type[asyncToken,] + _operands = Value[inputTokens...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvgpu.device_async_create_group", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`device_async_wait` + +The `nvgpu.device_async_wait` op will block the execution thread until the group +associated with the source token is fully completed. + +The optional `\$numGroups` attribute gives an upper bound of the number of +groups uncompleted when the wait can unblock the thread. For example, if +16 async groups are pushe and `\$numGroups` is set to 12, then the thread +will unblock when 12 groups or fewer are in flight (4 groups have +completed). + +# Example + +```mlir +nvgpu.device_async_wait %0 +``` +""" +function device_async_wait(asyncDependencies::Value; numGroups=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[asyncDependencies,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(numGroups) && push!(_attributes, namedattribute("numGroups", numGroups)) + + return IR.create_operation( + "nvgpu.device_async_wait", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ldmatrix` + +The `nvgpu.ldmatrix` op represents loading a matrix fragment from +memory to registers. The source and result type must be compatible +with lowering to the `nvvm.ldmatrix` instruction. This op represents +the distributed version of a `vector.transfer_read` as an intermediate +step between lowering from `vector.transfer_read` to `nvvm.ldmatrix`. + +This operation is meant to follow the semantic of described here: +https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#warp-level-matrix-instructions-ldmatrix + +# Example +```mlir +%0 = nvgpu.ldmatrix %sm[%c0, %c0] {numTiles = 4 : i32, transpose = false} : + memref -> vector<4x2xf16> +``` +""" +function ldmatrix( + srcMemref::Value, + indices::Vector{Value}; + res::IR.Type, + transpose, + numTiles, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[srcMemref, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("transpose", transpose), namedattribute("numTiles", numTiles) + ] + + return IR.create_operation( + "nvgpu.ldmatrix", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_arrive_expect_tx` + +A thread executing the Op performs an expect-tx operation on the mbarrier +object at the location specified by the address operand \$barrier. The +expect-tx operation, with an \$txcount argument, increases the tx-count of +an mbarrier object by the value specified by \$txcount. This makes the +current phase of the mbarrier object to expect and track the completion of +additional asynchronous transactions. + +The `\$txCount` specifies the number of element to the expect-tx operation. + +# Example +```mlir + nvgpu.mbarrier.arrive.expect_tx %barrier, %ic0 : !nvgpu.mbarrier.barrier> +``` +""" +function mbarrier_arrive_expect_tx( + barriers::Value, + txcount::Value, + mbarId::Value, + predicate=nothing::Union{Nothing,Value}; + location=Location(), +) + _results = IR.Type[] + _operands = Value[barriers, txcount, mbarId] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(predicate) && push!(_operands, predicate) + + return IR.create_operation( + "nvgpu.mbarrier.arrive.expect_tx", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_arrive_nocomplete` + +The Op performs arrive-on operation on the `mbarrier` object and returns a +`nvgpu.mbarrier.token`. + +The Op does not cause the `nvgpu.mbarrier` to complete its current phase. + +# Example +```mlir + %token = nvgpu.mbarrier.arrive.noComplete %barrier, %count : !nvgpu.mbarrier.barrier> -> !nvgpu.mbarrier.token +``` +""" +function mbarrier_arrive_nocomplete( + barriers::Value, mbarId::Value, count::Value; token::IR.Type, location=Location() +) + _results = IR.Type[token,] + _operands = Value[barriers, mbarId, count] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvgpu.mbarrier.arrive.nocomplete", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_arrive` + +The Op performs arrive-on operation on the `mbarrier` object and returns a +`nvgpu.mbarrier.token`. + +For more information, see +https://docs.nvidia.com/cuda/parallel-thread-execution/#arrive-on-operation-on-mbarrier-object + +# Example +```mlir + %token = nvgpu.mbarrier.arrive %barrier : !nvgpu.mbarrier.barrier> -> !nvgpu.mbarrier.token +``` +""" +function mbarrier_arrive( + barriers::Value, mbarId::Value; token::IR.Type, location=Location() +) + _results = IR.Type[token,] + _operands = Value[barriers, mbarId] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvgpu.mbarrier.arrive", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_create` + +The Op generates one or more `mbarrier` object, which is a barrier created in +shared memory and supports various synchronization behaviors for threads. + +The `mbarrier` object has the following type and alignment requirements: + Type: .b64, Alignment: 8, Memory space: .shared + +# Example +```mlir + %barrier = nvgpu.mbarrier.create -> !nvgpu.mbarrier.barrier> +``` +""" +function mbarrier_create(; barriers::IR.Type, location=Location()) + _results = IR.Type[barriers,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvgpu.mbarrier.create", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_init` + +The Op initializes the `mbarrier` object with the given number of threads. + +# Example +```mlir + %num_threads = gpu.block_dim x + %barrier = nvgpu.mbarrier.create -> !nvgpu.mbarrier.barrier> + nvgpu.mbarrier.init %barrier, %num_threads : !nvgpu.mbarrier.barrier> +``` +""" +function mbarrier_init( + barriers::Value, + count::Value, + mbarId::Value, + predicate=nothing::Union{Nothing,Value}; + location=Location(), +) + _results = IR.Type[] + _operands = Value[barriers, count, mbarId] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(predicate) && push!(_operands, predicate) + + return IR.create_operation( + "nvgpu.mbarrier.init", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_test_wait` + +Checks whether the mbarrier object has completed the phase. It is is a +non-blocking instruction which tests for the completion of the phase. + +# Example +```mlir + %isComplete = nvgpu.mbarrier.test.wait %barrier, %token : !nvgpu.mbarrier.barrier>, !nvgpu.mbarrier.token +``` +""" +function mbarrier_test_wait( + barriers::Value, token::Value, mbarId::Value; waitComplete::IR.Type, location=Location() +) + _results = IR.Type[waitComplete,] + _operands = Value[barriers, token, mbarId] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvgpu.mbarrier.test.wait", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mbarrier_try_wait_parity` + +Checks whether the mbarrier object has completed the phase. It is is a +potentially blocking instruction which tests for the completion of the +phase. Suspended thread resumes execution when the specified phase completes +OR before the phase completes following a system-dependent time limit. + +The `\$phaseParity` specifies either even phase (0) or odd phase (1) to +wait. + +# Example +```mlir + nvgpu.mbarrier.try_wait.parity %barrier, %phaseParity, %ticks : !nvgpu.mbarrier.barrier> +``` +""" +function mbarrier_try_wait_parity( + barriers::Value, phaseParity::Value, ticks::Value, mbarId::Value; location=Location() +) + _results = IR.Type[] + _operands = Value[barriers, phaseParity, ticks, mbarId] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvgpu.mbarrier.try_wait.parity", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mma_sp_sync` + +The `nvgu.mma.sp.sync` operation performs a warp-distributed MMA operation +where operand A is \"structured sparse\". In this case, the `matrixA` operand +represents the (warp-distributed) non-zero values of operand A, and the +`sparse_metadata` operand provides the indices. + +The full description of the sparsity storage format and distribution scheme is +described in the PTX docs. This operation is meant to follow the semantic +described in the PTX documentation here: +https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#warp-level-matrix-instructions-for-sparse-mma + +The way the indices are distributed among the threads in a warp is controlled +by the optional `sparsity_selector` operand, which is `0` by default. For +more information, please consult the PTX documentation linked above. + +Example (targetingthe f16 16x8x32 `mma.sp` PTX instruction): + +```mlir +nvgpu.mma.sp.sync (%a, %b, %c) metadata (%meta) {mmaShape = [16, 8, 32]} : + (vector<4x2xf16>, vector<2x2xf16>, vector<2x2xf16>) -> vector<2x2xf16> +``` +""" +function mma_sp_sync( + matrixA::Value, + matrixB::Value, + matrixC::Value, + sparseMetadata::Value; + res::IR.Type, + mmaShape, + sparsitySelector=nothing, + tf32Enabled=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[matrixA, matrixB, matrixC, sparseMetadata] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mmaShape", mmaShape),] + !isnothing(sparsitySelector) && + push!(_attributes, namedattribute("sparsitySelector", sparsitySelector)) + !isnothing(tf32Enabled) && + push!(_attributes, namedattribute("tf32Enabled", tf32Enabled)) + + return IR.create_operation( + "nvgpu.mma.sp.sync", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mma_sync` + +The `nvgpu.mma.sync` op represents the warp-level matrix-multiply-and- +accumulate (mma) operation that is compatible with `nvvm.mma.sync`. +The operands and results vector sizes are thread-level onwership to +the warp-level mma operation shape. `mmaShape` attribute holds the +warp-level matrix-multiply shape. + +The `nvgpu.mma.sync` op serves as an intermediate point between lowering from +`vector.contract` to `nvvm.mma.sync`. + +This operation is meant to follow the semantic of described here: + https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#warp-level-matrix-instructions-mma + +# Example + +```mlir +%res = nvgpu.mma.sync (%matrixA, %matrixB, %matrixC) {mmaShape = [16, 8, 16]} : + (vector<4x2xf16>, vector<2x2xf16>, vector<2x2xf32>) -> vector<2x2xf32> +``` +""" +function mma_sync( + matrixA::Value, + matrixB::Value, + matrixC::Value; + res::IR.Type, + mmaShape, + tf32Enabled=nothing, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[matrixA, matrixB, matrixC] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mmaShape", mmaShape),] + !isnothing(tf32Enabled) && + push!(_attributes, namedattribute("tf32Enabled", tf32Enabled)) + + return IR.create_operation( + "nvgpu.mma.sync", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tma_async_load` + +The Op loads a tile memory region from global memory to shared memory by +Tensor Memory Access (TMA). + +`\$tensorMapDescriptor` is tensor map descriptor which has information about +tile shape. The descriptor is created by `nvgpu.tma.create.descriptor` + +The Op uses `\$barrier` mbarrier based completion mechanism. +""" +function tma_async_load( + dst::Value, + barriers::Value, + tensorMapDescriptor::Value, + coordinates::Vector{Value}, + mbarId::Value, + multicastMask=nothing::Union{Nothing,Value}; + predicate=nothing::Union{Nothing,Value}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[dst, barriers, tensorMapDescriptor, coordinates..., mbarId] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(multicastMask) && push!(_operands, multicastMask) + !isnothing(predicate) && push!(_operands, predicate) + push!( + _attributes, + operandsegmentsizes([ + 1, + 1, + 1, + length(coordinates), + 1, + isnothing(multicastMask) ? 0 : 1, + isnothing(predicate) ? 0 : 1, + ]), + ) + + return IR.create_operation( + "nvgpu.tma.async.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tma_async_store` + +The Op store a tile memory region from global memory to shared memory by +Tensor Memory Access (TMA). + +`\$tensorMapDescriptor` is tensor map descriptor which has information about +tile shape. The descriptor is created by `nvgpu.tma.create.descriptor` +""" +function tma_async_store( + src::Value, + tensorMapDescriptor::Value, + coordinates::Vector{Value}, + predicate=nothing::Union{Nothing,Value}; + location=Location(), +) + _results = IR.Type[] + _operands = Value[src, tensorMapDescriptor, coordinates...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(predicate) && push!(_operands, predicate) + push!( + _attributes, + operandsegmentsizes([1, 1, length(coordinates), isnothing(predicate) ? 0 : 1]), + ) + + return IR.create_operation( + "nvgpu.tma.async.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tma_create_descriptor` + +The Op creates a tensor map descriptor object representing tiled memory +region. To do that it calls CUDA Driver\'s `cuTensorMapEncodeTiled`. The +descriptor is used by Tensor Memory Access (TMA). + +The `tensor` is the source tensor to be tiled. + +The `boxDimensions` is the size of the tiled memory region in each dimension. + +For more information see below: +https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__TENSOR__MEMORY.html +""" +function tma_create_descriptor( + tensor::Value, boxDimensions::Vector{Value}; tensorMap::IR.Type, location=Location() +) + _results = IR.Type[tensorMap,] + _operands = Value[tensor, boxDimensions...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvgpu.tma.create.descriptor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tma_prefetch_descriptor` + +The Op brings the cache line containing the given `\$tmaDescriptor` for +subsequent use by the `tma.async.load` instruction. +""" +function tma_prefetch_descriptor( + tensorMapDescriptor::Value, predicate=nothing::Union{Nothing,Value}; location=Location() +) + _results = IR.Type[] + _operands = Value[tensorMapDescriptor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(predicate) && push!(_operands, predicate) + + return IR.create_operation( + "nvgpu.tma.prefetch.descriptor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`warpgroup_generate_descriptor` + +This Op builds a `nvgpu.warpgroup.descriptor` that is used by +`nvgpu.warpgroup.mma` to perform warpgroup-level matrix multiply and +accumulate. + +The descriptor specifies the properties of the matrix in shared memory that +is a multiplicand in the matrix multiply and accumulate operation. +""" +function warpgroup_generate_descriptor( + tensor::Value, tensorMap::Value; descriptor::IR.Type, location=Location() +) + _results = IR.Type[descriptor,] + _operands = Value[tensor, tensorMap] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvgpu.warpgroup.generate.descriptor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`warpgroup_mma_init_accumulator` + +This Op generates and initializes the accumulator matrix for +`nvgpu.warpgroup.mma` op to perform matrix-multiply-and-accumulate. +""" +function warpgroup_mma_init_accumulator(; matrixC::IR.Type, location=Location()) + _results = IR.Type[matrixC,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvgpu.warpgroup.mma.init.accumulator", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`warpgroup_mma` + +The `nvgpu.warpgroup.mma` op performs the warpgroup-level (4 warps) +matrix-multiply-and-accumulate (mma) operation that results in +`nvvm.wgmma.mma_async`. + +The operands are `descriptorA` and `descriptorB` that are wgmma matrix +descriptors that shows the properties of the matrix in shared memory. The +results are thread-level ownership to the warpgroup-level mma operation +shape. The shape is deduced from the descriptor types and output vector. + +The Op encapsulates multiple `nvvm.wgmma.mma_async` operations to complete +the given shape. As `nvvm.wgmma.async` Op, or its corresponding PTX +instruction, is asynchronous, this Op groups the `nvvm.wgmma.async` and +surrounds them between `wgmma.fence.aligned` and +`wgmma.commit.group.sync.aligned`, `wgmma.wait.group.sync.aligned` Ops. + +# Example +```mlir + %r1,%r2 = nvgpu.warpgroup.mma %descA, %descB, %acc1, %acc2: + !nvgpu.warpgroup.descriptor>, + !nvgpu.warpgroup.descriptor>, + !nvgpu.warpgroup.accumulator>, + !nvgpu.warpgroup.accumulator> + -> + !nvgpu.warpgroup.accumulator>, + !nvgpu.warpgroup.accumulator> +``` +""" +function warpgroup_mma( + descriptorA::Value, + descriptorB::Value, + matrixC::Value; + matrixD::IR.Type, + waitGroup=nothing, + transposeA=nothing, + transposeB=nothing, + location=Location(), +) + _results = IR.Type[matrixD,] + _operands = Value[descriptorA, descriptorB, matrixC] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(waitGroup) && push!(_attributes, namedattribute("waitGroup", waitGroup)) + !isnothing(transposeA) && push!(_attributes, namedattribute("transposeA", transposeA)) + !isnothing(transposeB) && push!(_attributes, namedattribute("transposeB", transposeB)) + + return IR.create_operation( + "nvgpu.warpgroup.mma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`warpgroup_mma_store` + +The `nvgpu.warpgroup.mma.store` op performs the store of fragmented result +in \$matrixD to given memref. + +[See the details of register fragment layout for accumulator matrix D] +(https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#wgmma-64n16-d) + +Note that, the op must be run with warp group. +""" +function warpgroup_mma_store(matrixD::Value, dstMemref::Value; location=Location()) + _results = IR.Type[] + _operands = Value[matrixD, dstMemref] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "nvgpu.warpgroup.mma.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # nvgpu diff --git a/src/Dialects/19/OpenACC.jl b/src/Dialects/19/OpenACC.jl new file mode 100644 index 00000000..169c44a2 --- /dev/null +++ b/src/Dialects/19/OpenACC.jl @@ -0,0 +1,3177 @@ +module acc + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`atomic_capture` + +This operation performs an atomic capture. + +The region has the following allowed forms: + +``` + acc.atomic.capture { + acc.atomic.update ... + acc.atomic.read ... + acc.terminator + } + + acc.atomic.capture { + acc.atomic.read ... + acc.atomic.update ... + acc.terminator + } + + acc.atomic.capture { + acc.atomic.read ... + acc.atomic.write ... + acc.terminator + } +``` +""" +function atomic_capture(; region::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "acc.atomic.capture", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`atomic_read` + +This operation performs an atomic read. + +The operand `x` is the address from where the value is atomically read. +The operand `v` is the address where the value is stored after reading. +""" +function atomic_read(x::Value, v::Value; element_type, location=Location()) + _results = IR.Type[] + _operands = Value[x, v] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("element_type", element_type),] + + return IR.create_operation( + "acc.atomic.read", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`atomic_update` + +This operation performs an atomic update. + +The operand `x` is exactly the same as the operand `x` in the OpenACC +Standard (OpenACC 3.3, section 2.12). It is the address of the variable +that is being updated. `x` is atomically read/written. + +The region describes how to update the value of `x`. It takes the value at +`x` as an input and must yield the updated value. Only the update to `x` is +atomic. Generally the region must have only one instruction, but can +potentially have more than one instructions too. The update is sematically +similar to a compare-exchange loop based atomic update. + +The syntax of atomic update operation is different from atomic read and +atomic write operations. This is because only the host dialect knows how to +appropriately update a value. For example, while generating LLVM IR, if +there are no special `atomicrmw` instructions for the operation-type +combination in atomic update, a compare-exchange loop is generated, where +the core update operation is directly translated like regular operations by +the host dialect. The front-end must handle semantic checks for allowed +operations. +""" +function atomic_update(x::Value; region::Region, location=Location()) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "acc.atomic.update", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`atomic_write` + +This operation performs an atomic write. + +The operand `x` is the address to where the `expr` is atomically +written w.r.t. multiple threads. The evaluation of `expr` need not be +atomic w.r.t. the write to address. In general, the type(x) must +dereference to type(expr). +""" +function atomic_write(x::Value, expr::Value; location=Location()) + _results = IR.Type[] + _operands = Value[x, expr] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "acc.atomic.write", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`attach` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function attach( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.attach", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cache` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function cache( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.cache", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`copyin` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function copyin( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.copyin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`copyout` +- `varPtr`: The address of variable to copy back to. + - `accPtr`: The acc address of variable. This is the link from the data-entry + operation used. + - `bounds`: Used when copying just slice of array or array\'s bounds are not + encoded in type. They are in rank order where rank 0 is inner-most dimension. + - `asyncOperands` and `asyncOperandsDeviceType`: + pair-wise lists of the async clause values associated with device_type\'s. + - `asyncOnly`: a list of device_type\'s for which async clause + does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). + - `dataClause`: Keeps track of the data clause the user used. This is because + the acc operations are decomposed. So a \'copy\' clause is decomposed to both + `acc.copyin` and `acc.copyout` operations, but both have dataClause that + specifies `acc_copy` in this field. + - `structured`: Flag to note whether this is associated with structured region + (parallel, kernels, data) or unstructured (enter data, exit data). This is + important due to spec specifically calling out structured and dynamic reference + counters (2.6.7). + - `implicit`: Whether this is an implicitly generated operation, such as copies + done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. + - `name`: Holds the name of variable as specified in user clause (including bounds). + + The async values attached to the data exit operation imply that the data + action applies to all device types specified by the device_type clauses + using the activity queues on these devices as defined by the async values. +""" +function copyout( + accPtr::Value, + varPtr::Value, + bounds::Vector{Value}, + asyncOperands::Vector{Value}; + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[accPtr, varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([1, 1, length(bounds), length(asyncOperands)])) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.copyout", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function create( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.create", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bounds` + +This operation is used to record bounds used in acc data clause in a +normalized fashion (zero-based). This works well with the `PointerLikeType` +requirement in data clauses - since a `lowerbound` of 0 means looking +at data at the zero offset from pointer. + +The operation must have an `upperbound` or `extent` (or both are allowed - +but not checked for consistency). When the source language\'s arrays are +not zero-based, the `startIdx` must specify the zero-position index. + +Examples below show copying a slice of 10-element array except first element. +Note that the examples use extent in data clause for C++ and upperbound +for Fortran (as per 2.7.1). To simplify examples, the constants are used +directly in the acc.bounds operands - this is not the syntax of operation. + +C++: +``` +int array[10]; +#pragma acc copy(array[1:9]) +``` +=> +```mlir +acc.bounds lb(1) ub(9) extent(9) startIdx(0) +``` + +Fortran: +``` +integer :: array(1:10) +!\$acc copy(array(2:10)) +``` +=> +```mlir +acc.bounds lb(1) ub(9) extent(9) startIdx(1) +``` +""" +function bounds( + lowerbound=nothing::Union{Nothing,Value}; + upperbound=nothing::Union{Nothing,Value}, + extent=nothing::Union{Nothing,Value}, + stride=nothing::Union{Nothing,Value}, + startIdx=nothing::Union{Nothing,Value}, + result::IR.Type, + strideInBytes=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lowerbound) && push!(_operands, lowerbound) + !isnothing(upperbound) && push!(_operands, upperbound) + !isnothing(extent) && push!(_operands, extent) + !isnothing(stride) && push!(_operands, stride) + !isnothing(startIdx) && push!(_operands, startIdx) + push!( + _attributes, + operandsegmentsizes([ + isnothing(lowerbound) ? 0 : 1, + isnothing(upperbound) ? 0 : 1, + isnothing(extent) ? 0 : 1, + isnothing(stride) ? 0 : 1, + isnothing(startIdx) ? 0 : 1, + ]), + ) + !isnothing(strideInBytes) && + push!(_attributes, namedattribute("strideInBytes", strideInBytes)) + + return IR.create_operation( + "acc.bounds", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`data` + +The \"acc.data\" operation represents a data construct. It defines vars to +be allocated in the current device memory for the duration of the region, +whether data should be copied from local memory to the current device +memory upon region entry , and copied from device memory to local memory +upon region exit. + +# Example + +```mlir +acc.data present(%a: memref<10x10xf32>, %b: memref<10x10xf32>, + %c: memref<10xf32>, %d: memref<10xf32>) { + // data region +} +``` + +`async` and `wait` operands are supported with `device_type` information. +They should only be accessed by the extra provided getters. If modified, +the corresponding `device_type` attributes must be modified as well. +""" +function data( + ifCond=nothing::Union{Nothing,Value}; + asyncOperands::Vector{Value}, + waitOperands::Vector{Value}, + dataClauseOperands::Vector{Value}, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + waitOperandsSegments=nothing, + waitOperandsDeviceType=nothing, + hasWaitDevnum=nothing, + waitOnly=nothing, + defaultAttr=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncOperands..., waitOperands..., dataClauseOperands...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(ifCond) && push!(_operands, ifCond) + push!( + _attributes, + operandsegmentsizes([ + isnothing(ifCond) ? 0 : 1, + length(asyncOperands), + length(waitOperands), + length(dataClauseOperands), + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(waitOperandsSegments) && + push!(_attributes, namedattribute("waitOperandsSegments", waitOperandsSegments)) + !isnothing(waitOperandsDeviceType) && + push!(_attributes, namedattribute("waitOperandsDeviceType", waitOperandsDeviceType)) + !isnothing(hasWaitDevnum) && + push!(_attributes, namedattribute("hasWaitDevnum", hasWaitDevnum)) + !isnothing(waitOnly) && push!(_attributes, namedattribute("waitOnly", waitOnly)) + !isnothing(defaultAttr) && + push!(_attributes, namedattribute("defaultAttr", defaultAttr)) + + return IR.create_operation( + "acc.data", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`declare_device_resident` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function declare_device_resident( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.declare_device_resident", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`declare_enter` + +The \"acc.declare_enter\" operation represents the OpenACC declare directive +and captures the entry semantics to the implicit data region. +This operation is modeled similarly to \"acc.enter_data\". + +Example showing `acc declare create(a)`: + +```mlir +%0 = acc.create varPtr(%a : !llvm.ptr) -> !llvm.ptr +acc.declare_enter dataOperands(%0 : !llvm.ptr) +``` +""" +function declare_enter( + dataClauseOperands::Vector{Value}; token::IR.Type, location=Location() +) + _results = IR.Type[token,] + _operands = Value[dataClauseOperands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "acc.declare_enter", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`declare_exit` + +The \"acc.declare_exit\" operation represents the OpenACC declare directive +and captures the exit semantics from the implicit data region. +This operation is modeled similarly to \"acc.exit_data\". + +Example showing `acc declare device_resident(a)`: + +```mlir +%0 = acc.getdeviceptr varPtr(%a : !llvm.ptr) -> !llvm.ptr {dataClause = #acc} +acc.declare_exit dataOperands(%0 : !llvm.ptr) +acc.delete accPtr(%0 : !llvm.ptr) {dataClause = #acc} +``` +""" +function declare_exit( + token=nothing::Union{Nothing,Value}; + dataClauseOperands::Vector{Value}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[dataClauseOperands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(token) && push!(_operands, token) + push!( + _attributes, + operandsegmentsizes([isnothing(token) ? 0 : 1, length(dataClauseOperands)]), + ) + + return IR.create_operation( + "acc.declare_exit", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`declare_link` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function declare_link( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.declare_link", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`declare` + +The \"acc.declare\" operation represents an implicit declare region in +function (and subroutine in Fortran). + +# Example + +```mlir +%pa = acc.present varPtr(%a : memref<10x10xf32>) -> memref<10x10xf32> +acc.declare dataOperands(%pa: memref<10x10xf32>) { + // implicit region +} +``` +""" +function declare(dataClauseOperands::Vector{Value}; region::Region, location=Location()) + _results = IR.Type[] + _operands = Value[dataClauseOperands...,] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "acc.declare", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`delete` + +- `accPtr`: The acc address of variable. This is the link from the data-entry +operation used. +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data exit operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function delete( + accPtr::Value, + bounds::Vector{Value}, + asyncOperands::Vector{Value}; + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[accPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([1, length(bounds), length(asyncOperands)])) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.delete", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`detach` + +- `accPtr`: The acc address of variable. This is the link from the data-entry +operation used. +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data exit operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function detach( + accPtr::Value, + bounds::Vector{Value}, + asyncOperands::Vector{Value}; + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[accPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([1, length(bounds), length(asyncOperands)])) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.detach", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`deviceptr` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function deviceptr( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.deviceptr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`enter_data` + +The \"acc.enter_data\" operation represents the OpenACC enter data directive. + +# Example + +```mlir +acc.enter_data create(%d1 : memref<10xf32>) attributes {async} +``` +""" +function enter_data( + ifCond=nothing::Union{Nothing,Value}; + asyncOperand=nothing::Union{Nothing,Value}, + waitDevnum=nothing::Union{Nothing,Value}, + waitOperands::Vector{Value}, + dataClauseOperands::Vector{Value}, + async=nothing, + wait=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[waitOperands..., dataClauseOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(ifCond) && push!(_operands, ifCond) + !isnothing(asyncOperand) && push!(_operands, asyncOperand) + !isnothing(waitDevnum) && push!(_operands, waitDevnum) + push!( + _attributes, + operandsegmentsizes([ + isnothing(ifCond) ? 0 : 1, + isnothing(asyncOperand) ? 0 : 1, + isnothing(waitDevnum) ? 0 : 1, + length(waitOperands), + length(dataClauseOperands), + ]), + ) + !isnothing(async) && push!(_attributes, namedattribute("async", async)) + !isnothing(wait) && push!(_attributes, namedattribute("wait", wait)) + + return IR.create_operation( + "acc.enter_data", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`exit_data` + +The \"acc.exit_data\" operation represents the OpenACC exit data directive. + +# Example + +```mlir +acc.exit_data delete(%d1 : memref<10xf32>) attributes {async} +``` +""" +function exit_data( + ifCond=nothing::Union{Nothing,Value}; + asyncOperand=nothing::Union{Nothing,Value}, + waitDevnum=nothing::Union{Nothing,Value}, + waitOperands::Vector{Value}, + dataClauseOperands::Vector{Value}, + async=nothing, + wait=nothing, + finalize=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[waitOperands..., dataClauseOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(ifCond) && push!(_operands, ifCond) + !isnothing(asyncOperand) && push!(_operands, asyncOperand) + !isnothing(waitDevnum) && push!(_operands, waitDevnum) + push!( + _attributes, + operandsegmentsizes([ + isnothing(ifCond) ? 0 : 1, + isnothing(asyncOperand) ? 0 : 1, + isnothing(waitDevnum) ? 0 : 1, + length(waitOperands), + length(dataClauseOperands), + ]), + ) + !isnothing(async) && push!(_attributes, namedattribute("async", async)) + !isnothing(wait) && push!(_attributes, namedattribute("wait", wait)) + !isnothing(finalize) && push!(_attributes, namedattribute("finalize", finalize)) + + return IR.create_operation( + "acc.exit_data", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`firstprivate` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function firstprivate( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.firstprivate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`firstprivate_recipe` + +Declares an OpenACC privatization recipe with copy of the initial value. +The operation requires two mandatory regions and one optional. + + 1. The initializer region specifies how to allocate and initialize a new + private value. For example in Fortran, a derived-type might have a + default initialization. The region has an argument that contains the + value that need to be privatized. This is useful if the type is not + known at compile time and the private value is needed to create its + copy. + 2. The copy region specifies how to copy the initial value to the newly + created private value. It takes the initial value and the privatized + value as arguments. + 3. The destroy region specifies how to destruct the value when it reaches + its end of life. It takes the privatized value as argument. It is + optional. + +A single privatization recipe can be used for multiple operand if they have +the same type and do not require a specific default initialization. + +# Example + +```mlir +acc.firstprivate.recipe @privatization_f32 : f32 init { +^bb0(%0: f32): + // init region contains a sequence of operations to create and + // initialize the copy if needed. It yields the create copy. +} copy { +^bb0(%0: f32, %1: !llvm.ptr): + // copy region contains a sequence of operations to copy the initial value + // of the firstprivate value to the newly created value. +} destroy { +^bb0(%0: f32) + // destroy region contains a sequences of operations to destruct the + // created copy. +} + +// The privatization symbol is then used in the corresponding operation. +acc.parallel firstprivate(@privatization_f32 -> %a : f32) { +} +``` +""" +function firstprivate_recipe(; + sym_name, + type, + initRegion::Region, + copyRegion::Region, + destroyRegion::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[initRegion, copyRegion, destroyRegion] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("type", type) + ] + + return IR.create_operation( + "acc.firstprivate.recipe", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`getdeviceptr` + +This operation is used to get the `accPtr` for a variable. This is often +used in conjunction with data exit operations when the data entry +operation is not visible. This operation can have a `dataClause` argument +that is any of the valid `mlir::acc::DataClause` entries. +\\ + + Description of arguments: + - `varPtr`: The address of variable to copy. + - `varPtrPtr`: Specifies the address of varPtr - only used when the variable + copied is a field in a struct. This is important for OpenACC due to implicit + attach semantics on data clauses (2.6.4). + - `bounds`: Used when copying just slice of array or array\'s bounds are not + encoded in type. They are in rank order where rank 0 is inner-most dimension. + - `asyncOperands` and `asyncOperandsDeviceType`: + pair-wise lists of the async clause values associated with device_type\'s. + - `asyncOnly`: a list of device_type\'s for which async clause + does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). + - `dataClause`: Keeps track of the data clause the user used. This is because + the acc operations are decomposed. So a \'copy\' clause is decomposed to both + `acc.copyin` and `acc.copyout` operations, but both have dataClause that + specifies `acc_copy` in this field. + - `structured`: Flag to note whether this is associated with structured region + (parallel, kernels, data) or unstructured (enter data, exit data). This is + important due to spec specifically calling out structured and dynamic reference + counters (2.6.7). + - `implicit`: Whether this is an implicitly generated operation, such as copies + done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. + - `name`: Holds the name of variable as specified in user clause (including bounds). + + The async values attached to the data entry operation imply that the data + action applies to all device types specified by the device_type clauses + using the activity queues on these devices as defined by the async values. +""" +function getdeviceptr( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.getdeviceptr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`global_ctor` + +The \"acc.global_ctor\" operation is used to capture OpenACC actions to apply +on globals (such as `acc declare`) at the entry to the implicit data region. +This operation is isolated and intended to be used in a module. + +Example showing `declare create` of global: + +```mlir +llvm.mlir.global external @globalvar() : i32 { + %0 = llvm.mlir.constant(0 : i32) : i32 + llvm.return %0 : i32 +} +acc.global_ctor @acc_constructor { + %0 = llvm.mlir.addressof @globalvar : !llvm.ptr + %1 = acc.create varPtr(%0 : !llvm.ptr) -> !llvm.ptr + acc.declare_enter dataOperands(%1 : !llvm.ptr) +} +``` +""" +function global_ctor(; sym_name, region::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("sym_name", sym_name),] + + return IR.create_operation( + "acc.global_ctor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`global_dtor` + +The \"acc.global_dtor\" operation is used to capture OpenACC actions to apply +on globals (such as `acc declare`) at the exit from the implicit data +region. This operation is isolated and intended to be used in a module. + +Example showing delete associated with `declare create` of global: + +```mlir +llvm.mlir.global external @globalvar() : i32 { + %0 = llvm.mlir.constant(0 : i32) : i32 + llvm.return %0 : i32 +} +acc.global_dtor @acc_destructor { + %0 = llvm.mlir.addressof @globalvar : !llvm.ptr + %1 = acc.getdeviceptr varPtr(%0 : !llvm.ptr) -> !llvm.ptr {dataClause = #acc} + acc.declare_exit dataOperands(%1 : !llvm.ptr) + acc.delete accPtr(%1 : !llvm.ptr) {dataClause = #acc} +} +``` +""" +function global_dtor(; sym_name, region::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("sym_name", sym_name),] + + return IR.create_operation( + "acc.global_dtor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`host_data` + +The \"acc.host_data\" operation represents the OpenACC host_data construct. + +# Example + +```mlir +%0 = acc.use_device varPtr(%a : !llvm.ptr) -> !llvm.ptr +acc.host_data dataOperands(%0 : !llvm.ptr) { + +} +``` +""" +function host_data( + ifCond=nothing::Union{Nothing,Value}; + dataClauseOperands::Vector{Value}, + ifPresent=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[dataClauseOperands...,] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(ifCond) && push!(_operands, ifCond) + push!( + _attributes, + operandsegmentsizes([isnothing(ifCond) ? 0 : 1, length(dataClauseOperands)]), + ) + !isnothing(ifPresent) && push!(_attributes, namedattribute("ifPresent", ifPresent)) + + return IR.create_operation( + "acc.host_data", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`init` + +The \"acc.init\" operation represents the OpenACC init executable +directive. + +# Example + +```mlir +acc.init +acc.init device_num(%dev1 : i32) +``` +""" +function init( + deviceNumOperand=nothing::Union{Nothing,Value}; + ifCond=nothing::Union{Nothing,Value}, + device_types=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(deviceNumOperand) && push!(_operands, deviceNumOperand) + !isnothing(ifCond) && push!(_operands, ifCond) + push!( + _attributes, + operandsegmentsizes([ + isnothing(deviceNumOperand) ? 0 : 1, isnothing(ifCond) ? 0 : 1 + ]), + ) + !isnothing(device_types) && + push!(_attributes, namedattribute("device_types", device_types)) + + return IR.create_operation( + "acc.init", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`kernels` + +The \"acc.kernels\" operation represents a kernels construct block. It has +one region to be compiled into a sequence of kernels for execution on the +current device. + +# Example + +```mlir +acc.kernels num_gangs(%c10) num_workers(%c10) + private(%c : memref<10xf32>) { + // kernels region +} +``` + +`collapse`, `gang`, `worker`, `vector`, `seq`, `independent`, `auto` and +`tile` operands are supported with `device_type` information. They should +only be accessed by the extra provided getters. If modified, the +corresponding `device_type` attributes must be modified as well. +""" +function kernels( + asyncOperands::Vector{Value}, + waitOperands::Vector{Value}, + numGangs::Vector{Value}, + numWorkers::Vector{Value}, + vectorLength::Vector{Value}, + ifCond=nothing::Union{Nothing,Value}; + selfCond=nothing::Union{Nothing,Value}, + dataClauseOperands::Vector{Value}, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + waitOperandsSegments=nothing, + waitOperandsDeviceType=nothing, + hasWaitDevnum=nothing, + waitOnly=nothing, + numGangsSegments=nothing, + numGangsDeviceType=nothing, + numWorkersDeviceType=nothing, + vectorLengthDeviceType=nothing, + selfAttr=nothing, + defaultAttr=nothing, + combined=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ + asyncOperands..., + waitOperands..., + numGangs..., + numWorkers..., + vectorLength..., + dataClauseOperands..., + ] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(ifCond) && push!(_operands, ifCond) + !isnothing(selfCond) && push!(_operands, selfCond) + push!( + _attributes, + operandsegmentsizes([ + length(asyncOperands), + length(waitOperands), + length(numGangs), + length(numWorkers), + length(vectorLength), + isnothing(ifCond) ? 0 : 1, + isnothing(selfCond) ? 0 : 1, + length(dataClauseOperands), + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(waitOperandsSegments) && + push!(_attributes, namedattribute("waitOperandsSegments", waitOperandsSegments)) + !isnothing(waitOperandsDeviceType) && + push!(_attributes, namedattribute("waitOperandsDeviceType", waitOperandsDeviceType)) + !isnothing(hasWaitDevnum) && + push!(_attributes, namedattribute("hasWaitDevnum", hasWaitDevnum)) + !isnothing(waitOnly) && push!(_attributes, namedattribute("waitOnly", waitOnly)) + !isnothing(numGangsSegments) && + push!(_attributes, namedattribute("numGangsSegments", numGangsSegments)) + !isnothing(numGangsDeviceType) && + push!(_attributes, namedattribute("numGangsDeviceType", numGangsDeviceType)) + !isnothing(numWorkersDeviceType) && + push!(_attributes, namedattribute("numWorkersDeviceType", numWorkersDeviceType)) + !isnothing(vectorLengthDeviceType) && + push!(_attributes, namedattribute("vectorLengthDeviceType", vectorLengthDeviceType)) + !isnothing(selfAttr) && push!(_attributes, namedattribute("selfAttr", selfAttr)) + !isnothing(defaultAttr) && + push!(_attributes, namedattribute("defaultAttr", defaultAttr)) + !isnothing(combined) && push!(_attributes, namedattribute("combined", combined)) + + return IR.create_operation( + "acc.kernels", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop` + +The \"acc.loop\" operation represents the OpenACC loop construct. The lower +and upper bounds specify a half-open range: the range includes the lower +bound but does not include the upper bound. If the `inclusive` attribute is +set then the upper bound is included. + +# Example + +```mlir +acc.loop gang() vector() (%arg3 : index, %arg4 : index, %arg5 : index) = + (%c0, %c0, %c0 : index, index, index) to + (%c10, %c10, %c10 : index, index, index) step + (%c1, %c1, %c1 : index, index, index) { + // Loop body + acc.yield +} attributes { collapse = [3] } +``` + +`collapse`, `gang`, `worker`, `vector`, `seq`, `independent`, `auto` and +`tile` operands are supported with `device_type` information. They should +only be accessed by the extra provided getters. If modified, the +corresponding `device_type` attributes must be modified as well. +""" +function loop( + lowerbound::Vector{Value}, + upperbound::Vector{Value}, + step::Vector{Value}, + gangOperands::Vector{Value}, + workerNumOperands::Vector{Value}, + vectorOperands::Vector{Value}, + tileOperands::Vector{Value}, + cacheOperands::Vector{Value}, + privateOperands::Vector{Value}, + reductionOperands::Vector{Value}; + results::Vector{IR.Type}, + inclusiveUpperbound=nothing, + collapse=nothing, + collapseDeviceType=nothing, + gangOperandsArgType=nothing, + gangOperandsSegments=nothing, + gangOperandsDeviceType=nothing, + workerNumOperandsDeviceType=nothing, + vectorOperandsDeviceType=nothing, + seq=nothing, + independent=nothing, + auto_=nothing, + gang=nothing, + worker=nothing, + vector=nothing, + tileOperandsSegments=nothing, + tileOperandsDeviceType=nothing, + privatizations=nothing, + reductionRecipes=nothing, + combined=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[ + lowerbound..., + upperbound..., + step..., + gangOperands..., + workerNumOperands..., + vectorOperands..., + tileOperands..., + cacheOperands..., + privateOperands..., + reductionOperands..., + ] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!( + _attributes, + operandsegmentsizes([ + length(lowerbound), + length(upperbound), + length(step), + length(gangOperands), + length(workerNumOperands), + length(vectorOperands), + length(tileOperands), + length(cacheOperands), + length(privateOperands), + length(reductionOperands), + ]), + ) + !isnothing(inclusiveUpperbound) && + push!(_attributes, namedattribute("inclusiveUpperbound", inclusiveUpperbound)) + !isnothing(collapse) && push!(_attributes, namedattribute("collapse", collapse)) + !isnothing(collapseDeviceType) && + push!(_attributes, namedattribute("collapseDeviceType", collapseDeviceType)) + !isnothing(gangOperandsArgType) && + push!(_attributes, namedattribute("gangOperandsArgType", gangOperandsArgType)) + !isnothing(gangOperandsSegments) && + push!(_attributes, namedattribute("gangOperandsSegments", gangOperandsSegments)) + !isnothing(gangOperandsDeviceType) && + push!(_attributes, namedattribute("gangOperandsDeviceType", gangOperandsDeviceType)) + !isnothing(workerNumOperandsDeviceType) && push!( + _attributes, + namedattribute("workerNumOperandsDeviceType", workerNumOperandsDeviceType), + ) + !isnothing(vectorOperandsDeviceType) && push!( + _attributes, + namedattribute("vectorOperandsDeviceType", vectorOperandsDeviceType), + ) + !isnothing(seq) && push!(_attributes, namedattribute("seq", seq)) + !isnothing(independent) && + push!(_attributes, namedattribute("independent", independent)) + !isnothing(auto_) && push!(_attributes, namedattribute("auto_", auto_)) + !isnothing(gang) && push!(_attributes, namedattribute("gang", gang)) + !isnothing(worker) && push!(_attributes, namedattribute("worker", worker)) + !isnothing(vector) && push!(_attributes, namedattribute("vector", vector)) + !isnothing(tileOperandsSegments) && + push!(_attributes, namedattribute("tileOperandsSegments", tileOperandsSegments)) + !isnothing(tileOperandsDeviceType) && + push!(_attributes, namedattribute("tileOperandsDeviceType", tileOperandsDeviceType)) + !isnothing(privatizations) && + push!(_attributes, namedattribute("privatizations", privatizations)) + !isnothing(reductionRecipes) && + push!(_attributes, namedattribute("reductionRecipes", reductionRecipes)) + !isnothing(combined) && push!(_attributes, namedattribute("combined", combined)) + + return IR.create_operation( + "acc.loop", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`nocreate` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function nocreate( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.nocreate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`parallel` + +The \"acc.parallel\" operation represents a parallel construct block. It has +one region to be executed in parallel on the current device. + +# Example + +```mlir +acc.parallel num_gangs(%c10) num_workers(%c10) + private(%c : memref<10xf32>) { + // parallel region +} +``` + +`async`, `wait`, `num_gangs`, `num_workers` and `vector_length` operands are +supported with `device_type` information. They should only be accessed by +the extra provided getters. If modified, the corresponding `device_type` +attributes must be modified as well. +""" +function parallel( + asyncOperands::Vector{Value}, + waitOperands::Vector{Value}, + numGangs::Vector{Value}, + numWorkers::Vector{Value}, + vectorLength::Vector{Value}, + ifCond=nothing::Union{Nothing,Value}; + selfCond=nothing::Union{Nothing,Value}, + reductionOperands::Vector{Value}, + gangPrivateOperands::Vector{Value}, + gangFirstPrivateOperands::Vector{Value}, + dataClauseOperands::Vector{Value}, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + waitOperandsSegments=nothing, + waitOperandsDeviceType=nothing, + hasWaitDevnum=nothing, + waitOnly=nothing, + numGangsSegments=nothing, + numGangsDeviceType=nothing, + numWorkersDeviceType=nothing, + vectorLengthDeviceType=nothing, + selfAttr=nothing, + reductionRecipes=nothing, + privatizations=nothing, + firstprivatizations=nothing, + defaultAttr=nothing, + combined=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ + asyncOperands..., + waitOperands..., + numGangs..., + numWorkers..., + vectorLength..., + reductionOperands..., + gangPrivateOperands..., + gangFirstPrivateOperands..., + dataClauseOperands..., + ] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(ifCond) && push!(_operands, ifCond) + !isnothing(selfCond) && push!(_operands, selfCond) + push!( + _attributes, + operandsegmentsizes([ + length(asyncOperands), + length(waitOperands), + length(numGangs), + length(numWorkers), + length(vectorLength), + isnothing(ifCond) ? 0 : 1, + isnothing(selfCond) ? 0 : 1, + length(reductionOperands), + length(gangPrivateOperands), + length(gangFirstPrivateOperands), + length(dataClauseOperands), + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(waitOperandsSegments) && + push!(_attributes, namedattribute("waitOperandsSegments", waitOperandsSegments)) + !isnothing(waitOperandsDeviceType) && + push!(_attributes, namedattribute("waitOperandsDeviceType", waitOperandsDeviceType)) + !isnothing(hasWaitDevnum) && + push!(_attributes, namedattribute("hasWaitDevnum", hasWaitDevnum)) + !isnothing(waitOnly) && push!(_attributes, namedattribute("waitOnly", waitOnly)) + !isnothing(numGangsSegments) && + push!(_attributes, namedattribute("numGangsSegments", numGangsSegments)) + !isnothing(numGangsDeviceType) && + push!(_attributes, namedattribute("numGangsDeviceType", numGangsDeviceType)) + !isnothing(numWorkersDeviceType) && + push!(_attributes, namedattribute("numWorkersDeviceType", numWorkersDeviceType)) + !isnothing(vectorLengthDeviceType) && + push!(_attributes, namedattribute("vectorLengthDeviceType", vectorLengthDeviceType)) + !isnothing(selfAttr) && push!(_attributes, namedattribute("selfAttr", selfAttr)) + !isnothing(reductionRecipes) && + push!(_attributes, namedattribute("reductionRecipes", reductionRecipes)) + !isnothing(privatizations) && + push!(_attributes, namedattribute("privatizations", privatizations)) + !isnothing(firstprivatizations) && + push!(_attributes, namedattribute("firstprivatizations", firstprivatizations)) + !isnothing(defaultAttr) && + push!(_attributes, namedattribute("defaultAttr", defaultAttr)) + !isnothing(combined) && push!(_attributes, namedattribute("combined", combined)) + + return IR.create_operation( + "acc.parallel", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`present` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function present( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.present", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`private` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function private( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.private", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`private_recipe` + +Declares an OpenACC privatization recipe. The operation requires one +mandatory and one optional region. + + 1. The initializer region specifies how to allocate and initialize a new + private value. For example in Fortran, a derived-type might have a + default initialization. The region has an argument that contains the + value that need to be privatized. This is useful if the type is not + known at compile time and the private value is needed to create its + copy. + 2. The destroy region specifies how to destruct the value when it reaches + its end of life. It takes the privatized value as argument. + +A single privatization recipe can be used for multiple operand if they have +the same type and do not require a specific default initialization. + +# Example + +```mlir +acc.private.recipe @privatization_f32 : f32 init { +^bb0(%0: f32): + // init region contains a sequence of operations to create and + // initialize the copy if needed. It yields the create copy. +} destroy { +^bb0(%0: f32) + // destroy region contains a sequences of operations to destruct the + // created copy. +} + +// The privatization symbol is then used in the corresponding operation. +acc.parallel private(@privatization_f32 -> %a : f32) { +} +``` +""" +function private_recipe(; + sym_name, type, initRegion::Region, destroyRegion::Region, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[initRegion, destroyRegion] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("type", type) + ] + + return IR.create_operation( + "acc.private.recipe", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reduction` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function reduction( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.reduction", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reduction_recipe` + +Declares an OpenACC reduction recipe. The operation requires two +mandatory regions. + + 1. The initializer region specifies how to initialize the local reduction + value. The region has a first argument that contains the value of the + reduction accumulator at the start of the reduction. It is expected to + `acc.yield` the new value. Extra arguments can be added to deal with + dynamic arrays. + 2. The reduction region contains a sequences of operations to combine two + values of the reduction type into one. It has at least two arguments + and it is expected to `acc.yield` the combined value. Extra arguments + can be added to deal with dynamic arrays. + +# Example + +```mlir +acc.reduction.recipe @reduction_add_i64 : i64 reduction_operator init { +^bb0(%0: i64): + // init region contains a sequence of operations to initialize the local + // reduction value as specified in 2.5.15 + %c0 = arith.constant 0 : i64 + acc.yield %c0 : i64 +} combiner { +^bb0(%0: i64, %1: i64) + // combiner region contains a sequence of operations to combine + // two values into one. + %2 = arith.addi %0, %1 : i64 + acc.yield %2 : i64 +} + +// The reduction symbol is then used in the corresponding operation. +acc.parallel reduction(@reduction_add_i64 -> %a : i64) { +} +``` + +The following table lists the valid operators and the initialization values +according to OpenACC 3.3: + +|------------------------------------------------| +| C/C++ | Fortran | +|-----------------------|------------------------| +| operator | init value | operator | init value | +| + | 0 | + | 0 | +| * | 1 | * | 1 | +| max | least | max | least | +| min | largest | min | largest | +| & | ~0 | iand | all bits on | +| | | 0 | ior | 0 | +| ^ | 0 | ieor | 0 | +| && | 1 | .and. | .true. | +| || | 0 | .or. | .false. | +| | | .eqv. | .true. | +| | | .neqv. | .false. | +-------------------------------------------------| +""" +function reduction_recipe(; + sym_name, + type, + reductionOperator, + initRegion::Region, + combinerRegion::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[initRegion, combinerRegion] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), + namedattribute("type", type), + namedattribute("reductionOperator", reductionOperator), + ] + + return IR.create_operation( + "acc.reduction.recipe", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`routine` + +The `acc.routine` operation is used to capture the clauses of acc +routine directive, including the associated function name. The associated +function keeps track of its corresponding routine declaration through +the `RoutineInfoAttr`. + +# Example + +```mlir +func.func @acc_func(%a : i64) -> () attributes + {acc.routine_info = #acc.routine_info<[@acc_func_rout1]>} { + return +} +acc.routine @acc_func_rout1 func(@acc_func) gang +``` + +`bind`, `gang`, `worker`, `vector` and `seq` operands are supported with +`device_type` information. They should only be accessed by the extra +provided getters. If modified, the corresponding `device_type` attributes +must be modified as well. +""" +function routine(; + sym_name, + func_name, + bindName=nothing, + bindNameDeviceType=nothing, + worker=nothing, + vector=nothing, + seq=nothing, + nohost=nothing, + implicit=nothing, + gang=nothing, + gangDim=nothing, + gangDimDeviceType=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("func_name", func_name) + ] + !isnothing(bindName) && push!(_attributes, namedattribute("bindName", bindName)) + !isnothing(bindNameDeviceType) && + push!(_attributes, namedattribute("bindNameDeviceType", bindNameDeviceType)) + !isnothing(worker) && push!(_attributes, namedattribute("worker", worker)) + !isnothing(vector) && push!(_attributes, namedattribute("vector", vector)) + !isnothing(seq) && push!(_attributes, namedattribute("seq", seq)) + !isnothing(nohost) && push!(_attributes, namedattribute("nohost", nohost)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(gang) && push!(_attributes, namedattribute("gang", gang)) + !isnothing(gangDim) && push!(_attributes, namedattribute("gangDim", gangDim)) + !isnothing(gangDimDeviceType) && + push!(_attributes, namedattribute("gangDimDeviceType", gangDimDeviceType)) + + return IR.create_operation( + "acc.routine", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`serial` + +The \"acc.serial\" operation represents a serial construct block. It has +one region to be executed in serial on the current device. + +# Example + +```mlir +acc.serial private(%c : memref<10xf32>) { + // serial region +} +``` + +`async` and `wait` operands are supported with `device_type` information. +They should only be accessed by the extra provided getters. If modified, +the corresponding `device_type` attributes must be modified as well. +""" +function serial( + asyncOperands::Vector{Value}, + waitOperands::Vector{Value}, + ifCond=nothing::Union{Nothing,Value}; + selfCond=nothing::Union{Nothing,Value}, + reductionOperands::Vector{Value}, + gangPrivateOperands::Vector{Value}, + gangFirstPrivateOperands::Vector{Value}, + dataClauseOperands::Vector{Value}, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + waitOperandsSegments=nothing, + waitOperandsDeviceType=nothing, + hasWaitDevnum=nothing, + waitOnly=nothing, + selfAttr=nothing, + reductionRecipes=nothing, + privatizations=nothing, + firstprivatizations=nothing, + defaultAttr=nothing, + combined=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ + asyncOperands..., + waitOperands..., + reductionOperands..., + gangPrivateOperands..., + gangFirstPrivateOperands..., + dataClauseOperands..., + ] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(ifCond) && push!(_operands, ifCond) + !isnothing(selfCond) && push!(_operands, selfCond) + push!( + _attributes, + operandsegmentsizes([ + length(asyncOperands), + length(waitOperands), + isnothing(ifCond) ? 0 : 1, + isnothing(selfCond) ? 0 : 1, + length(reductionOperands), + length(gangPrivateOperands), + length(gangFirstPrivateOperands), + length(dataClauseOperands), + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(waitOperandsSegments) && + push!(_attributes, namedattribute("waitOperandsSegments", waitOperandsSegments)) + !isnothing(waitOperandsDeviceType) && + push!(_attributes, namedattribute("waitOperandsDeviceType", waitOperandsDeviceType)) + !isnothing(hasWaitDevnum) && + push!(_attributes, namedattribute("hasWaitDevnum", hasWaitDevnum)) + !isnothing(waitOnly) && push!(_attributes, namedattribute("waitOnly", waitOnly)) + !isnothing(selfAttr) && push!(_attributes, namedattribute("selfAttr", selfAttr)) + !isnothing(reductionRecipes) && + push!(_attributes, namedattribute("reductionRecipes", reductionRecipes)) + !isnothing(privatizations) && + push!(_attributes, namedattribute("privatizations", privatizations)) + !isnothing(firstprivatizations) && + push!(_attributes, namedattribute("firstprivatizations", firstprivatizations)) + !isnothing(defaultAttr) && + push!(_attributes, namedattribute("defaultAttr", defaultAttr)) + !isnothing(combined) && push!(_attributes, namedattribute("combined", combined)) + + return IR.create_operation( + "acc.serial", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`set` + +The \"acc.set\" operation represents the OpenACC set directive. + +# Example + +```mlir +acc.set device_num(%dev1 : i32) +``` +""" +function set( + defaultAsync=nothing::Union{Nothing,Value}; + deviceNum=nothing::Union{Nothing,Value}, + ifCond=nothing::Union{Nothing,Value}, + device_type=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(defaultAsync) && push!(_operands, defaultAsync) + !isnothing(deviceNum) && push!(_operands, deviceNum) + !isnothing(ifCond) && push!(_operands, ifCond) + push!( + _attributes, + operandsegmentsizes([ + isnothing(defaultAsync) ? 0 : 1, + isnothing(deviceNum) ? 0 : 1, + isnothing(ifCond) ? 0 : 1, + ]), + ) + !isnothing(device_type) && + push!(_attributes, namedattribute("device_type", device_type)) + + return IR.create_operation( + "acc.set", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`shutdown` + +The \"acc.shutdown\" operation represents the OpenACC shutdown executable +directive. + +# Example + +```mlir +acc.shutdown +acc.shutdown device_num(%dev1 : i32) +``` +""" +function shutdown( + deviceNumOperand=nothing::Union{Nothing,Value}; + ifCond=nothing::Union{Nothing,Value}, + device_types=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(deviceNumOperand) && push!(_operands, deviceNumOperand) + !isnothing(ifCond) && push!(_operands, ifCond) + push!( + _attributes, + operandsegmentsizes([ + isnothing(deviceNumOperand) ? 0 : 1, isnothing(ifCond) ? 0 : 1 + ]), + ) + !isnothing(device_types) && + push!(_attributes, namedattribute("device_types", device_types)) + + return IR.create_operation( + "acc.shutdown", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`terminator` + +A terminator operation for regions that appear in the body of OpenACC +operation. Generic OpenACC construct regions are not expected to return any +value so the terminator takes no operands. The terminator op returns control +to the enclosing op. +""" +function terminator(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "acc.terminator", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`update_device` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function update_device( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.update_device", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`update_host` +- `varPtr`: The address of variable to copy back to. + - `accPtr`: The acc address of variable. This is the link from the data-entry + operation used. + - `bounds`: Used when copying just slice of array or array\'s bounds are not + encoded in type. They are in rank order where rank 0 is inner-most dimension. + - `asyncOperands` and `asyncOperandsDeviceType`: + pair-wise lists of the async clause values associated with device_type\'s. + - `asyncOnly`: a list of device_type\'s for which async clause + does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). + - `dataClause`: Keeps track of the data clause the user used. This is because + the acc operations are decomposed. So a \'copy\' clause is decomposed to both + `acc.copyin` and `acc.copyout` operations, but both have dataClause that + specifies `acc_copy` in this field. + - `structured`: Flag to note whether this is associated with structured region + (parallel, kernels, data) or unstructured (enter data, exit data). This is + important due to spec specifically calling out structured and dynamic reference + counters (2.6.7). + - `implicit`: Whether this is an implicitly generated operation, such as copies + done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. + - `name`: Holds the name of variable as specified in user clause (including bounds). + + The async values attached to the data exit operation imply that the data + action applies to all device types specified by the device_type clauses + using the activity queues on these devices as defined by the async values. +""" +function update_host( + accPtr::Value, + varPtr::Value, + bounds::Vector{Value}, + asyncOperands::Vector{Value}; + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[accPtr, varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + push!(_attributes, operandsegmentsizes([1, 1, length(bounds), length(asyncOperands)])) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.update_host", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`update` + +The `acc.update` operation represents the OpenACC update executable +directive. +As host and self clauses are synonyms, any operands for host and self are +add to \$hostOperands. + +# Example + +```mlir +acc.update device(%d1 : memref<10xf32>) attributes {async} +``` + +`async` and `wait` operands are supported with `device_type` information. +They should only be accessed by the extra provided getters. If modified, +the corresponding `device_type` attributes must be modified as well. +""" +function update( + ifCond=nothing::Union{Nothing,Value}; + asyncOperands::Vector{Value}, + waitOperands::Vector{Value}, + dataClauseOperands::Vector{Value}, + asyncOperandsDeviceType=nothing, + async=nothing, + waitOperandsSegments=nothing, + waitOperandsDeviceType=nothing, + hasWaitDevnum=nothing, + waitOnly=nothing, + ifPresent=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[asyncOperands..., waitOperands..., dataClauseOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(ifCond) && push!(_operands, ifCond) + push!( + _attributes, + operandsegmentsizes([ + isnothing(ifCond) ? 0 : 1, + length(asyncOperands), + length(waitOperands), + length(dataClauseOperands), + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(async) && push!(_attributes, namedattribute("async", async)) + !isnothing(waitOperandsSegments) && + push!(_attributes, namedattribute("waitOperandsSegments", waitOperandsSegments)) + !isnothing(waitOperandsDeviceType) && + push!(_attributes, namedattribute("waitOperandsDeviceType", waitOperandsDeviceType)) + !isnothing(hasWaitDevnum) && + push!(_attributes, namedattribute("hasWaitDevnum", hasWaitDevnum)) + !isnothing(waitOnly) && push!(_attributes, namedattribute("waitOnly", waitOnly)) + !isnothing(ifPresent) && push!(_attributes, namedattribute("ifPresent", ifPresent)) + + return IR.create_operation( + "acc.update", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`use_device` + +Description of arguments: +- `varPtr`: The address of variable to copy. +- `varPtrPtr`: Specifies the address of varPtr - only used when the variable +copied is a field in a struct. This is important for OpenACC due to implicit +attach semantics on data clauses (2.6.4). +- `bounds`: Used when copying just slice of array or array\'s bounds are not +encoded in type. They are in rank order where rank 0 is inner-most dimension. +- `asyncOperands` and `asyncOperandsDeviceType`: +pair-wise lists of the async clause values associated with device_type\'s. +- `asyncOnly`: a list of device_type\'s for which async clause +does not specify a value (default is acc_async_noval - OpenACC 3.3 2.16.1). +- `dataClause`: Keeps track of the data clause the user used. This is because +the acc operations are decomposed. So a \'copy\' clause is decomposed to both +`acc.copyin` and `acc.copyout` operations, but both have dataClause that +specifies `acc_copy` in this field. +- `structured`: Flag to note whether this is associated with structured region +(parallel, kernels, data) or unstructured (enter data, exit data). This is +important due to spec specifically calling out structured and dynamic reference +counters (2.6.7). +- `implicit`: Whether this is an implicitly generated operation, such as copies +done to satisfy \"Variables with Implicitly Determined Data Attributes\" in 2.6.2. +- `name`: Holds the name of variable as specified in user clause (including bounds). + +The async values attached to the data entry operation imply that the data +action applies to all device types specified by the device_type clauses +using the activity queues on these devices as defined by the async values. +""" +function use_device( + varPtr::Value, + varPtrPtr=nothing::Union{Nothing,Value}; + bounds::Vector{Value}, + asyncOperands::Vector{Value}, + accPtr::IR.Type, + asyncOperandsDeviceType=nothing, + asyncOnly=nothing, + dataClause=nothing, + structured=nothing, + implicit=nothing, + name=nothing, + location=Location(), +) + _results = IR.Type[accPtr,] + _operands = Value[varPtr, bounds..., asyncOperands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(varPtrPtr) && push!(_operands, varPtrPtr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(varPtrPtr) ? 0 : 1, length(bounds), length(asyncOperands) + ]), + ) + !isnothing(asyncOperandsDeviceType) && push!( + _attributes, namedattribute("asyncOperandsDeviceType", asyncOperandsDeviceType) + ) + !isnothing(asyncOnly) && push!(_attributes, namedattribute("asyncOnly", asyncOnly)) + !isnothing(dataClause) && push!(_attributes, namedattribute("dataClause", dataClause)) + !isnothing(structured) && push!(_attributes, namedattribute("structured", structured)) + !isnothing(implicit) && push!(_attributes, namedattribute("implicit", implicit)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "acc.use_device", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wait` + +The \"acc.wait\" operation represents the OpenACC wait executable +directive. + +# Example + +```mlir +acc.wait(%value1: index) +acc.wait() async(%async1: i32) +``` + +acc.wait does not implement MemoryEffects interface, +so it affects all the resources. This is conservatively +correct. More precise modelling of the memory effects +seems to be impossible without the whole program analysis. +""" +function wait( + waitOperands::Vector{Value}, + asyncOperand=nothing::Union{Nothing,Value}; + waitDevnum=nothing::Union{Nothing,Value}, + ifCond=nothing::Union{Nothing,Value}, + async=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[waitOperands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(asyncOperand) && push!(_operands, asyncOperand) + !isnothing(waitDevnum) && push!(_operands, waitDevnum) + !isnothing(ifCond) && push!(_operands, ifCond) + push!( + _attributes, + operandsegmentsizes([ + length(waitOperands), + isnothing(asyncOperand) ? 0 : 1, + isnothing(waitDevnum) ? 0 : 1, + isnothing(ifCond) ? 0 : 1, + ]), + ) + !isnothing(async) && push!(_attributes, namedattribute("async", async)) + + return IR.create_operation( + "acc.wait", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +`acc.yield` is a special terminator operation for block inside regions in +various acc ops (including parallel, loop, atomic.update). It returns values +to the immediately enclosing acc op. +""" +function yield(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "acc.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # acc diff --git a/src/Dialects/19/OpenMP.jl b/src/Dialects/19/OpenMP.jl new file mode 100644 index 00000000..8b76cb37 --- /dev/null +++ b/src/Dialects/19/OpenMP.jl @@ -0,0 +1,2428 @@ +module omp + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`atomic_capture` + +This operation performs an atomic capture. + +The region has the following allowed forms: +``` + omp.atomic.capture { + omp.atomic.update ... + omp.atomic.read ... + omp.terminator + } + + omp.atomic.capture { + omp.atomic.read ... + omp.atomic.update ... + omp.terminator + } + + omp.atomic.capture { + omp.atomic.read ... + omp.atomic.write ... + omp.terminator + } +``` + +`hint` is the value of hint (as specified in the hint clause). It is a +compile time constant. As the name suggests, this is just a hint for +optimization. + +`memory_order` indicates the memory ordering behavior of the construct. It +can be one of `seq_cst`, `acq_rel`, `release`, `acquire` or `relaxed`. +""" +function atomic_capture(; + hint_val=nothing, memory_order_val=nothing, region::Region, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(hint_val) && push!(_attributes, namedattribute("hint_val", hint_val)) + !isnothing(memory_order_val) && + push!(_attributes, namedattribute("memory_order_val", memory_order_val)) + + return IR.create_operation( + "omp.atomic.capture", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`atomic_read` + +This operation performs an atomic read. + +The operand `x` is the address from where the value is atomically read. +The operand `v` is the address where the value is stored after reading. + +`hint` is the value of hint (as specified in the hint clause). It is a +compile time constant. As the name suggests, this is just a hint for +optimization. + +`memory_order` indicates the memory ordering behavior of the construct. It +can be one of `seq_cst`, `acq_rel`, `release`, `acquire` or `relaxed`. +""" +function atomic_read( + x::Value, + v::Value; + element_type, + hint_val=nothing, + memory_order_val=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x, v] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("element_type", element_type),] + !isnothing(hint_val) && push!(_attributes, namedattribute("hint_val", hint_val)) + !isnothing(memory_order_val) && + push!(_attributes, namedattribute("memory_order_val", memory_order_val)) + + return IR.create_operation( + "omp.atomic.read", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`atomic_update` + +This operation performs an atomic update. + +The operand `x` is exactly the same as the operand `x` in the OpenMP +Standard (OpenMP 5.0, section 2.17.7). It is the address of the variable +that is being updated. `x` is atomically read/written. + +The region describes how to update the value of `x`. It takes the value at +`x` as an input and must yield the updated value. Only the update to `x` is +atomic. Generally the region must have only one instruction, but can +potentially have more than one instructions too. The update is sematically +similar to a compare-exchange loop based atomic update. + +The syntax of atomic update operation is different from atomic read and +atomic write operations. This is because only the host dialect knows how to +appropriately update a value. For example, while generating LLVM IR, if +there are no special `atomicrmw` instructions for the operation-type +combination in atomic update, a compare-exchange loop is generated, where +the core update operation is directly translated like regular operations by +the host dialect. The front-end must handle semantic checks for allowed +operations. + +`hint` is the value of hint (as specified in the hint clause). It is a +compile time constant. As the name suggests, this is just a hint for +optimization. + +`memory_order` indicates the memory ordering behavior of the construct. It +can be one of `seq_cst`, `acq_rel`, `release`, `acquire` or `relaxed`. +""" +function atomic_update( + x::Value; + hint_val=nothing, + memory_order_val=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(hint_val) && push!(_attributes, namedattribute("hint_val", hint_val)) + !isnothing(memory_order_val) && + push!(_attributes, namedattribute("memory_order_val", memory_order_val)) + + return IR.create_operation( + "omp.atomic.update", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`atomic_write` + +This operation performs an atomic write. + +The operand `x` is the address to where the `expr` is atomically +written w.r.t. multiple threads. The evaluation of `expr` need not be +atomic w.r.t. the write to address. In general, the type(x) must +dereference to type(expr). + +`hint` is the value of hint (as specified in the hint clause). It is a +compile time constant. As the name suggests, this is just a hint for +optimization. + +`memory_order` indicates the memory ordering behavior of the construct. It +can be one of `seq_cst`, `acq_rel`, `release`, `acquire` or `relaxed`. +""" +function atomic_write( + x::Value, expr::Value; hint_val=nothing, memory_order_val=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[x, expr] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(hint_val) && push!(_attributes, namedattribute("hint_val", hint_val)) + !isnothing(memory_order_val) && + push!(_attributes, namedattribute("memory_order_val", memory_order_val)) + + return IR.create_operation( + "omp.atomic.write", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`barrier` + +The barrier construct specifies an explicit barrier at the point at which +the construct appears. +""" +function barrier(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "omp.barrier", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cancel` + +The cancel construct activates cancellation of the innermost enclosing +region of the type specified. +""" +function cancel( + if_expr=nothing::Union{Nothing,Value}; + cancellation_construct_type_val, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute( + "cancellation_construct_type_val", cancellation_construct_type_val + ),] + !isnothing(if_expr) && push!(_operands, if_expr) + + return IR.create_operation( + "omp.cancel", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cancellation_point` + +The cancellation point construct introduces a user-defined cancellation +point at which implicit or explicit tasks check if cancellation of the +innermost enclosing region of the type specified has been activated. +""" +function cancellation_point(; cancellation_construct_type_val, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute( + "cancellation_construct_type_val", cancellation_construct_type_val + ),] + + return IR.create_operation( + "omp.cancellation_point", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`critical_declare` + +Declares a named critical section. + +The `sym_name` can be used in `omp.critical` constructs in the dialect. + +`hint` is the value of hint (as specified in the hint clause). It is a +compile time constant. As the name suggests, this is just a hint for +optimization. +""" +function critical_declare(; sym_name, hint_val=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("sym_name", sym_name),] + !isnothing(hint_val) && push!(_attributes, namedattribute("hint_val", hint_val)) + + return IR.create_operation( + "omp.critical.declare", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`critical` + +The critical construct imposes a restriction on the associated structured +block (region) to be executed by only a single thread at a time. + +The optional `name` argument of critical constructs is used to identify +them. Unnamed critical constructs behave as though an identical name was +specified. +""" +function critical(; name=nothing, region::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "omp.critical", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`declare_reduction` + +Declares an OpenMP reduction kind. This requires two mandatory and two +optional regions. + + 1. The initializer region specifies how to initialize the thread-local + reduction value. This is usually the neutral element of the reduction. + For convenience, the region has an argument that contains the value + of the reduction accumulator at the start of the reduction. It is + expected to `omp.yield` the new value on all control flow paths. + 2. The reduction region specifies how to combine two values into one, i.e. + the reduction operator. It accepts the two values as arguments and is + expected to `omp.yield` the combined value on all control flow paths. + 3. The atomic reduction region is optional and specifies how two values + can be combined atomically given local accumulator variables. It is + expected to store the combined value in the first accumulator variable. + 4. The cleanup region is optional and specifies how to clean up any memory + allocated by the initializer region. The region has an argument that + contains the value of the thread-local reduction accumulator. This will + be executed after the reduction has completed. + +Note that the MLIR type system does not allow for type-polymorphic +reductions. Separate reduction declarations should be created for different +element and accumulator types. + +For initializer and reduction regions, the operand to `omp.yield` must +match the parent operation\'s results. +""" +function declare_reduction(; + sym_name, + type, + initializerRegion::Region, + reductionRegion::Region, + atomicReductionRegion::Region, + cleanupRegion::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[ + initializerRegion, reductionRegion, atomicReductionRegion, cleanupRegion + ] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("type", type) + ] + + return IR.create_operation( + "omp.declare_reduction", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`distribute` + +The distribute construct specifies that the iterations of one or more loops +(optionally specified using collapse clause) will be executed by the +initial teams in the context of their implicit tasks. The loops that the +distribute op is associated with starts with the outermost loop enclosed by +the distribute op region and going down the loop nest toward the innermost +loop. The iterations are distributed across the initial threads of all +initial teams that execute the teams region to which the distribute region +binds. + +The distribute loop construct specifies that the iterations of the loop(s) +will be executed in parallel by threads in the current context. These +iterations are spread across threads that already exist in the enclosing +region. + +The body region can only contain a single block which must contain a single +operation and a terminator. The operation must be another compatible loop +wrapper or an `omp.loop_nest`. + +```mlir +omp.distribute { + omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { + %a = load %arrA[%i1, %i2] : memref + %b = load %arrB[%i1, %i2] : memref + %sum = arith.addf %a, %b : f32 + store %sum, %arrC[%i1, %i2] : memref + omp.yield + } + omp.terminator +} +``` + +The `dist_schedule_static` attribute specifies the schedule for this loop, +determining how the loop is distributed across the various teams. The +optional `chunk_size` associated with this determines further controls this +distribution. + +The `allocators_vars` and `allocate_vars` parameters are a variadic list of +values that specify the memory allocator to be used to obtain storage for +private values. + +The optional `order` attribute specifies which order the iterations of the +associated loops are executed in. Currently the only option for this +attribute is \"concurrent\". +""" +function distribute( + chunk_size=nothing::Union{Nothing,Value}; + allocate_vars::Vector{Value}, + allocators_vars::Vector{Value}, + dist_schedule_static=nothing, + order_val=nothing, + order_mod=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[allocate_vars..., allocators_vars...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(chunk_size) && push!(_operands, chunk_size) + push!( + _attributes, + operandsegmentsizes([ + isnothing(chunk_size) ? 0 : 1, length(allocate_vars), length(allocators_vars) + ]), + ) + !isnothing(dist_schedule_static) && + push!(_attributes, namedattribute("dist_schedule_static", dist_schedule_static)) + !isnothing(order_val) && push!(_attributes, namedattribute("order_val", order_val)) + !isnothing(order_mod) && push!(_attributes, namedattribute("order_mod", order_mod)) + + return IR.create_operation( + "omp.distribute", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`flush` + +The flush construct executes the OpenMP flush operation. This operation +makes a thread\'s temporary view of memory consistent with memory and +enforces an order on the memory operations of the variables explicitly +specified or implied. +""" +function flush(varList::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[varList...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "omp.flush", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop_nest` + +This operation represents a collapsed rectangular loop nest. For each +rectangular loop of the nest represented by an instance of this operation, +lower and upper bounds, as well as a step variable, must be defined. + +The lower and upper bounds specify a half-open range: the range includes the +lower bound but does not include the upper bound. If the `inclusive` +attribute is specified then the upper bound is also included. + +The body region can contain any number of blocks. The region is terminated +by an `omp.yield` instruction without operands. The induction variables, +represented as entry block arguments to the loop nest operation\'s single +region, match the types of the `lowerBound`, `upperBound` and `step` +arguments. + +```mlir +omp.loop_nest (%i1, %i2) : i32 = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { + %a = load %arrA[%i1, %i2] : memref + %b = load %arrB[%i1, %i2] : memref + %sum = arith.addf %a, %b : f32 + store %sum, %arrC[%i1, %i2] : memref + omp.yield +} +``` + +This is a temporary simplified definition of a loop based on existing OpenMP +loop operations intended to serve as a stopgap solution until the long-term +representation of canonical loops is defined. Specifically, this operation +is intended to serve as a unique source for loop information during the +transition to making `omp.distribute`, `omp.simd`, `omp.taskloop` and +`omp.wsloop` wrapper operations. It is not intended to help with the +addition of support for loop transformations, non-rectangular loops and +non-perfectly nested loops. +""" +function loop_nest( + lowerBound::Vector{Value}, + upperBound::Vector{Value}, + step::Vector{Value}; + inclusive=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lowerBound..., upperBound..., step...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(inclusive) && push!(_attributes, namedattribute("inclusive", inclusive)) + + return IR.create_operation( + "omp.loop_nest", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`map_bounds` + +This operation is a variation on the OpenACC dialects DataBoundsOp. Within +the OpenMP dialect it stores the bounds/range of data to be mapped to a +device specified by map clauses on target directives. Within +the OpenMP dialect, the MapBoundsOp is associated with MapInfoOp, +helping to store bounds information for the mapped variable. + +It is used to support OpenMP array sectioning, Fortran pointer and +allocatable mapping and pointer/allocatable member of derived types. +In all cases the MapBoundsOp holds information on the section of +data to be mapped. Such as the upper bound and lower bound of the +section of data to be mapped. This information is currently +utilised by the LLVM-IR lowering to help generate instructions to +copy data to and from the device when processing target operations. + +The example below copys a section of a 10-element array; all except the +first element, utilising OpenMP array sectioning syntax where array +subscripts are provided to specify the bounds to be mapped to device. +To simplify the examples, the constants are used directly, in reality +they will be MLIR SSA values. + +C++: +``` +int array[10]; +#pragma target map(array[1:9]) +``` +=> +```mlir +omp.map.bounds lower_bound(1) upper_bound(9) extent(9) start_idx(0) +``` + +Fortran: +``` +integer :: array(1:10) +!\$target map(array(2:10)) +``` +=> +```mlir +omp.map.bounds lower_bound(1) upper_bound(9) extent(9) start_idx(1) +``` + +For Fortran pointers and allocatables (as well as those that are +members of derived types) the bounds information is provided by +the Fortran compiler and runtime through descriptor information. + +A basic pointer example can be found below (constants again +provided for simplicity, where in reality SSA values will be +used, in this case that point to data yielded by Fortran\'s +descriptors): + +Fortran: +``` +integer, pointer :: ptr(:) +allocate(ptr(10)) +!\$target map(ptr) +``` +=> +```mlir +omp.map.bounds lower_bound(0) upper_bound(9) extent(10) start_idx(1) +``` + +This operation records the bounds information in a normalized fashion +(zero-based). This works well with the `PointerLikeType` +requirement in data clauses - since a `lower_bound` of 0 means looking +at data at the zero offset from pointer. + +This operation must have an `upper_bound` or `extent` (or both are allowed - +but not checked for consistency). When the source language\'s arrays are +not zero-based, the `start_idx` must specify the zero-position index. +""" +function map_bounds( + lower_bound=nothing::Union{Nothing,Value}; + upper_bound=nothing::Union{Nothing,Value}, + extent=nothing::Union{Nothing,Value}, + stride=nothing::Union{Nothing,Value}, + start_idx=nothing::Union{Nothing,Value}, + result::IR.Type, + stride_in_bytes=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lower_bound) && push!(_operands, lower_bound) + !isnothing(upper_bound) && push!(_operands, upper_bound) + !isnothing(extent) && push!(_operands, extent) + !isnothing(stride) && push!(_operands, stride) + !isnothing(start_idx) && push!(_operands, start_idx) + push!( + _attributes, + operandsegmentsizes([ + isnothing(lower_bound) ? 0 : 1, + isnothing(upper_bound) ? 0 : 1, + isnothing(extent) ? 0 : 1, + isnothing(stride) ? 0 : 1, + isnothing(start_idx) ? 0 : 1, + ]), + ) + !isnothing(stride_in_bytes) && + push!(_attributes, namedattribute("stride_in_bytes", stride_in_bytes)) + + return IR.create_operation( + "omp.map.bounds", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`map_info` + +The MapInfoOp captures information relating to individual OpenMP map clauses +that are applied to certain OpenMP directives such as Target and Target Data. + +For example, the map type modifier; such as from, tofrom and to, the variable +being captured or the bounds of an array section being mapped. + +It can be used to capture both implicit and explicit map information, where +explicit is an argument directly specified to an OpenMP map clause or implicit +where a variable is utilised in a target region but is defined externally to +the target region. + +This map information is later used to aid the lowering of the target operations +they are attached to providing argument input and output context for kernels +generated or the target data mapping environment. + +Example (Fortran): + +``` +integer :: index +!\$target map(to: index) +``` +=> +```mlir +omp.map.info var_ptr(%index_ssa) map_type(to) map_capture_type(ByRef) + name(index) +``` + +Description of arguments: +- `var_ptr`: The address of variable to copy. +- `var_type`: The type of the variable to copy. +- `var_ptr_ptr`: Used when the variable copied is a member of a class, structure + or derived type and refers to the originating struct. +- `members`: Used to indicate mapped child members for the current MapInfoOp, + represented as other MapInfoOp\'s, utilised in cases where a parent structure + type and members of the structure type are being mapped at the same time. + For example: map(to: parent, parent->member, parent->member2[:10]) +- `members_index`: Used to indicate the ordering of members within the containing + parent (generally a record type such as a structure, class or derived type), + e.g. struct {int x, float y, double z}, x would be 0, y would be 1, and z + would be 2. This aids the mapping. +- `bounds`: Used when copying slices of array\'s, pointers or pointer members of + objects (e.g. derived types or classes), indicates the bounds to be copied + of the variable. When it\'s an array slice it is in rank order where rank 0 + is the inner-most dimension. +- \'map_clauses\': OpenMP map type for this map capture, for example: from, to and + always. It\'s a bitfield composed of the OpenMP runtime flags stored in + OpenMPOffloadMappingFlags. +- \'map_capture_type\': Capture type for the variable e.g. this, byref, byvalue, byvla + this can affect how the variable is lowered. +- `name`: Holds the name of variable as specified in user clause (including bounds). +- `partial_map`: The record type being mapped will not be mapped in its entirety, + it may be used however, in a mapping to bind it\'s mapped components together. +""" +function map_info( + var_ptr::Value, + var_ptr_ptr=nothing::Union{Nothing,Value}; + members::Vector{Value}, + bounds::Vector{Value}, + omp_ptr::IR.Type, + var_type, + members_index=nothing, + map_type=nothing, + map_capture_type=nothing, + name=nothing, + partial_map=nothing, + location=Location(), +) + _results = IR.Type[omp_ptr,] + _operands = Value[var_ptr, members..., bounds...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("var_type", var_type),] + !isnothing(var_ptr_ptr) && push!(_operands, var_ptr_ptr) + push!( + _attributes, + operandsegmentsizes([ + 1, isnothing(var_ptr_ptr) ? 0 : 1, length(members), length(bounds) + ]), + ) + !isnothing(members_index) && + push!(_attributes, namedattribute("members_index", members_index)) + !isnothing(map_type) && push!(_attributes, namedattribute("map_type", map_type)) + !isnothing(map_capture_type) && + push!(_attributes, namedattribute("map_capture_type", map_capture_type)) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + !isnothing(partial_map) && + push!(_attributes, namedattribute("partial_map", partial_map)) + + return IR.create_operation( + "omp.map.info", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`masked` + +Masked construct allows to specify a structured block to be executed by a subset of +threads of the current team. + +If `filter` is specified, the masked construct masks the execution of +the region to only the thread id filtered. Other threads executing the +parallel region are not expected to execute the region specified within +the `masked` directive. If `filter` is not specified, master thread is +expected to execute the region enclosed within `masked` directive. +""" +function masked( + filtered_thread_id=nothing::Union{Nothing,Value}; region::Region, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(filtered_thread_id) && push!(_operands, filtered_thread_id) + + return IR.create_operation( + "omp.masked", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`master` + +The master construct specifies a structured block that is executed by +the master thread of the team. +""" +function master(; region::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "omp.master", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ordered` + +The ordered construct without region is a stand-alone directive that +specifies cross-iteration dependencies in a doacross loop nest. + +The `depend_type_val` attribute refers to either the DEPEND(SOURCE) clause +or the DEPEND(SINK: vec) clause. + +The `num_loops_val` attribute specifies the number of loops in the doacross +nest. + +The `depend_vec_vars` is a variadic list of operands that specifies the +index of the loop iterator in the doacross nest for the DEPEND(SOURCE) +clause or the index of the element of \"vec\" for the DEPEND(SINK: vec) +clause. It contains the operands in multiple \"vec\" when multiple +DEPEND(SINK: vec) clauses exist in one ORDERED directive. +""" +function ordered( + depend_vec_vars::Vector{Value}; + depend_type_val=nothing, + num_loops_val=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[depend_vec_vars...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(depend_type_val) && + push!(_attributes, namedattribute("depend_type_val", depend_type_val)) + !isnothing(num_loops_val) && + push!(_attributes, namedattribute("num_loops_val", num_loops_val)) + + return IR.create_operation( + "omp.ordered", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ordered_region` + +The ordered construct with region specifies a structured block in a +worksharing-loop, SIMD, or worksharing-loop SIMD region that is executed in +the order of the loop iterations. + +The `simd` attribute corresponds to the simd clause specified. If it is not +present, it behaves as if the threads clause is specified or no clause is +specified. +""" +function ordered_region(; simd=nothing, region::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(simd) && push!(_attributes, namedattribute("simd", simd)) + + return IR.create_operation( + "omp.ordered.region", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`parallel` + +The parallel construct includes a region of code which is to be executed +by a team of threads. + +The optional `if_expr` parameter specifies a boolean result of a +conditional check. If this value is 1 or is not provided then the parallel +region runs as normal, if it is 0 then the parallel region is executed with +one thread. + +The optional `num_threads_var` parameter specifies the number of threads +which should be used to execute the parallel region. + +The `allocators_vars` and `allocate_vars` parameters are a variadic list of +values that specify the memory allocator to be used to obtain storage for +private values. + +Reductions can be performed by specifying reduction accumulator variables in +`reduction_vars`, symbols referring to reduction declarations in the +`reductions` attribute, and whether the reduction variable should be passed +into the reduction region by value or by reference in +`reduction_vars_byref`. Each reduction is identified by the accumulator it +uses and accumulators must not be repeated in the same reduction. A private +variable corresponding to the accumulator is used in place of the +accumulator inside the body of the operation. The reduction declaration +specifies how to combine the values from each iteration, section, team, +thread or simd lane defined by the operation\'s region into the final value, +which is available in the accumulator after they all complete. + +The optional `proc_bind_val` attribute controls the thread affinity for the +execution of the parallel region. +""" +function parallel( + if_expr=nothing::Union{Nothing,Value}; + num_threads_var=nothing::Union{Nothing,Value}, + allocate_vars::Vector{Value}, + allocators_vars::Vector{Value}, + reduction_vars::Vector{Value}, + private_vars::Vector{Value}, + reduction_vars_byref=nothing, + reductions=nothing, + proc_bind_val=nothing, + privatizers=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ + allocate_vars..., allocators_vars..., reduction_vars..., private_vars... + ] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(if_expr) && push!(_operands, if_expr) + !isnothing(num_threads_var) && push!(_operands, num_threads_var) + push!( + _attributes, + operandsegmentsizes([ + isnothing(if_expr) ? 0 : 1, + isnothing(num_threads_var) ? 0 : 1, + length(allocate_vars), + length(allocators_vars), + length(reduction_vars), + length(private_vars), + ]), + ) + !isnothing(reduction_vars_byref) && + push!(_attributes, namedattribute("reduction_vars_byref", reduction_vars_byref)) + !isnothing(reductions) && push!(_attributes, namedattribute("reductions", reductions)) + !isnothing(proc_bind_val) && + push!(_attributes, namedattribute("proc_bind_val", proc_bind_val)) + !isnothing(privatizers) && + push!(_attributes, namedattribute("privatizers", privatizers)) + + return IR.create_operation( + "omp.parallel", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`private` + +This operation provides a declaration of how to implement the +[first]privatization of a variable. The dialect users should provide +information about how to create an instance of the type in the alloc region, +how to initialize the copy from the original item in the copy region, and if +needed, how to deallocate allocated memory in the dealloc region. + +Examples: + +* `private(x)` would be emitted as: +```mlir +omp.private {type = private} @x.privatizer : !fir.ref alloc { +^bb0(%arg0: !fir.ref): +%0 = ... allocate proper memory for the private clone ... +omp.yield(%0 : !fir.ref) +} +``` + +* `firstprivate(x)` would be emitted as: +```mlir +omp.private {type = firstprivate} @x.privatizer : !fir.ref alloc { +^bb0(%arg0: !fir.ref): +%0 = ... allocate proper memory for the private clone ... +omp.yield(%0 : !fir.ref) +} copy { +^bb0(%arg0: !fir.ref, %arg1: !fir.ref): +// %arg0 is the original host variable. Same as for `alloc`. +// %arg1 represents the memory allocated in `alloc`. +... copy from host to the privatized clone .... +omp.yield(%arg1 : !fir.ref) +} +``` + +* `private(x)` for \"allocatables\" would be emitted as: +```mlir +omp.private {type = private} @x.privatizer : !some.type alloc { +^bb0(%arg0: !some.type): +%0 = ... allocate proper memory for the private clone ... +omp.yield(%0 : !fir.ref) +} dealloc { +^bb0(%arg0: !some.type): +... deallocate allocated memory ... +omp.yield +} +``` + +There are no restrictions on the body except for: +- The `alloc` & `dealloc` regions have a single argument. +- The `copy` region has 2 arguments. +- All three regions are terminated by `omp.yield` ops. +The above restrictions and other obvious restrictions (e.g. verifying the +type of yielded values) are verified by the custom op verifier. The actual +contents of the blocks inside all regions are not verified. + +Instances of this op would then be used by ops that model directives that +accept data-sharing attribute clauses. + +The \$sym_name attribute provides a symbol by which the privatizer op can be +referenced by other dialect ops. + +The \$type attribute is the type of the value being privatized. + +The \$data_sharing_type attribute specifies whether privatizer corresponds +to a `private` or a `firstprivate` clause. +""" +function private(; + sym_name, + type, + data_sharing_type, + alloc_region::Region, + copy_region::Region, + dealloc_region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[alloc_region, copy_region, dealloc_region] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), + namedattribute("type", type), + namedattribute("data_sharing_type", data_sharing_type), + ] + + return IR.create_operation( + "omp.private", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`section` + +A section operation encloses a region which represents one section in a +sections construct. A section op should always be surrounded by an +`omp.sections` operation. The section operation may have block args +which corespond to the block arguments of the surrounding `omp.sections` +operation. This is done to reflect situations where these block arguments +represent variables private to each section. +""" +function section(; region::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "omp.section", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sections` + +The sections construct is a non-iterative worksharing construct that +contains `omp.section` operations. The `omp.section` operations are to be +distributed among and executed by the threads in a team. Each `omp.section` +is executed once by one of the threads in the team in the context of its +implicit task. +Block arguments for reduction variables should be mirrored in enclosed +`omp.section` operations. + +Reductions can be performed by specifying reduction accumulator variables in +`reduction_vars`, symbols referring to reduction declarations in the +`reductions` attribute, and whether the reduction variable should be passed +into the reduction region by value or by reference in +`reduction_vars_byref`. Each reduction is identified by the accumulator it +uses and accumulators must not be repeated in the same reduction. A private +variable corresponding to the accumulator is used in place of the +accumulator inside the body of the operation. The reduction declaration +specifies how to combine the values from each iteration, section, team, +thread or simd lane defined by the operation\'s region into the final value, +which is available in the accumulator after they all complete. + +The `allocators_vars` and `allocate_vars` parameters are a variadic list of +values that specify the memory allocator to be used to obtain storage for +private values. + +The optional `nowait` attribute, when present, eliminates the implicit +barrier at the end of the construct, so the parent operation can make +progress even if the child operation has not completed yet. +""" +function sections( + reduction_vars::Vector{Value}, + allocate_vars::Vector{Value}, + allocators_vars::Vector{Value}; + reduction_vars_byref=nothing, + reductions=nothing, + nowait=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[reduction_vars..., allocate_vars..., allocators_vars...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!( + _attributes, + operandsegmentsizes([ + length(reduction_vars), length(allocate_vars), length(allocators_vars) + ]), + ) + !isnothing(reduction_vars_byref) && + push!(_attributes, namedattribute("reduction_vars_byref", reduction_vars_byref)) + !isnothing(reductions) && push!(_attributes, namedattribute("reductions", reductions)) + !isnothing(nowait) && push!(_attributes, namedattribute("nowait", nowait)) + + return IR.create_operation( + "omp.sections", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`simd` + +The simd construct can be applied to a loop to indicate that the loop can be +transformed into a SIMD loop (that is, multiple iterations of the loop can +be executed concurrently using SIMD instructions). + +The body region can only contain a single block which must contain a single +operation and a terminator. The operation must be another compatible loop +wrapper or an `omp.loop_nest`. + +``` +omp.simd { + omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { + %a = load %arrA[%i1, %i2] : memref + %b = load %arrB[%i1, %i2] : memref + %sum = arith.addf %a, %b : f32 + store %sum, %arrC[%i1, %i2] : memref + omp.yield + } + omp.terminator +} +``` + +When an if clause is present and evaluates to false, the preferred number of +iterations to be executed concurrently is one, regardless of whether +a simdlen clause is specified. + +The `alignment_values` attribute additionally specifies alignment of each +corresponding aligned operand. Note that `aligned_vars` and +`alignment_values` should contain the same number of elements. + +The optional `nontemporal` attribute specifies variables which have low +temporal locality across the iterations where they are accessed. + +The optional `order` attribute specifies which order the iterations of the +associated loops are executed in. Currently the only option for this +attribute is \"concurrent\". + +The `safelen` clause specifies that no two concurrent iterations within a +SIMD chunk can have a distance in the logical iteration space that is +greater than or equal to the value given in the clause. + +When a `simdlen` clause is present, the preferred number of iterations to be +executed concurrently is the value provided to the `simdlen` clause. +""" +function simd( + aligned_vars::Vector{Value}, + if_expr=nothing::Union{Nothing,Value}; + nontemporal_vars::Vector{Value}, + alignment_values=nothing, + order_val=nothing, + order_mod=nothing, + safelen=nothing, + simdlen=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[aligned_vars..., nontemporal_vars...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(if_expr) && push!(_operands, if_expr) + push!( + _attributes, + operandsegmentsizes([ + length(aligned_vars), isnothing(if_expr) ? 0 : 1, length(nontemporal_vars) + ]), + ) + !isnothing(alignment_values) && + push!(_attributes, namedattribute("alignment_values", alignment_values)) + !isnothing(order_val) && push!(_attributes, namedattribute("order_val", order_val)) + !isnothing(order_mod) && push!(_attributes, namedattribute("order_mod", order_mod)) + !isnothing(safelen) && push!(_attributes, namedattribute("safelen", safelen)) + !isnothing(simdlen) && push!(_attributes, namedattribute("simdlen", simdlen)) + + return IR.create_operation( + "omp.simd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`single` + +The single construct specifies that the associated structured block is +executed by only one of the threads in the team (not necessarily the +master thread), in the context of its implicit task. The other threads +in the team, which do not execute the block, wait at an implicit barrier +at the end of the single construct. + +The `allocators_vars` and `allocate_vars` parameters are a variadic list of +values that specify the memory allocator to be used to obtain storage for +private values. + +If `copyprivate` variables and functions are specified, then each thread +variable is updated with the variable value of the thread that executed +the single region, using the specified copy functions. + +The optional `nowait` attribute, when present, eliminates the implicit +barrier at the end of the construct, so the parent operation can make +progress even if the child operation has not completed yet. +""" +function single( + allocate_vars::Vector{Value}, + allocators_vars::Vector{Value}, + copyprivate_vars::Vector{Value}; + copyprivate_funcs=nothing, + nowait=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[allocate_vars..., allocators_vars..., copyprivate_vars...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!( + _attributes, + operandsegmentsizes([ + length(allocate_vars), length(allocators_vars), length(copyprivate_vars) + ]), + ) + !isnothing(copyprivate_funcs) && + push!(_attributes, namedattribute("copyprivate_funcs", copyprivate_funcs)) + !isnothing(nowait) && push!(_attributes, namedattribute("nowait", nowait)) + + return IR.create_operation( + "omp.single", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`target_data` + +Map variables to a device data environment for the extent of the region. + +The omp target data directive maps variables to a device data +environment, and defines the lexical scope of the data environment +that is created. The omp target data directive can reduce data copies +to and from the offloading device when multiple target regions are using +the same data. + +The optional `if_expr` parameter specifies a boolean result of a +conditional check. If this value is 1 or is not provided then the target +region runs on a device, if it is 0 then the target region is executed +on the host device. + +The optional `device` parameter specifies the device number for the target +region. + +The optional `use_device_ptr` specifies the device pointers to the +corresponding list items in the device data environment. + +The optional `use_device_addr` specifies the address of the objects in the +device data environment. + +The optional `map_operands` maps data from the current task\'s data +environment to the device data environment. +""" +function target_data( + if_expr=nothing::Union{Nothing,Value}; + device=nothing::Union{Nothing,Value}, + use_device_ptr::Vector{Value}, + use_device_addr::Vector{Value}, + map_operands::Vector{Value}, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[use_device_ptr..., use_device_addr..., map_operands...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(if_expr) && push!(_operands, if_expr) + !isnothing(device) && push!(_operands, device) + push!( + _attributes, + operandsegmentsizes([ + isnothing(if_expr) ? 0 : 1, + isnothing(device) ? 0 : 1, + length(use_device_ptr), + length(use_device_addr), + length(map_operands), + ]), + ) + + return IR.create_operation( + "omp.target_data", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`target_enter_data` + +The target enter data directive specifies that variables are mapped to +a device data environment. The target enter data directive is a +stand-alone directive. + +The optional `if_expr` parameter specifies a boolean result of a +conditional check. If this value is 1 or is not provided then the target +region runs on a device, if it is 0 then the target region is executed on +the host device. + +The optional `device` parameter specifies the device number for the target +region. + +The `depends` and `depend_vars` arguments are variadic lists of values that +specify the dependencies of this particular task in relation to other tasks. + +The optional `nowait` attribute, when present, eliminates the implicit +barrier at the end of the construct, so the parent operation can make +progress even if the child operation has not completed yet. + +The optional `map_operands` maps data from the current task\'s data +environment to the device data environment. +""" +function target_enter_data( + if_expr=nothing::Union{Nothing,Value}; + device=nothing::Union{Nothing,Value}, + depend_vars::Vector{Value}, + map_operands::Vector{Value}, + depends=nothing, + nowait=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[depend_vars..., map_operands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(if_expr) && push!(_operands, if_expr) + !isnothing(device) && push!(_operands, device) + push!( + _attributes, + operandsegmentsizes([ + isnothing(if_expr) ? 0 : 1, + isnothing(device) ? 0 : 1, + length(depend_vars), + length(map_operands), + ]), + ) + !isnothing(depends) && push!(_attributes, namedattribute("depends", depends)) + !isnothing(nowait) && push!(_attributes, namedattribute("nowait", nowait)) + + return IR.create_operation( + "omp.target_enter_data", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`target_exit_data` + +The target exit data directive specifies that variables are mapped to a +device data environment. The target exit data directive is +a stand-alone directive. + +The optional `if_expr` parameter specifies a boolean result of a +conditional check. If this value is 1 or is not provided then the target +region runs on a device, if it is 0 then the target region is executed +on the host device. + +The optional `device` parameter specifies the device number for the target +region. + +The `depends` and `depend_vars` arguments are variadic lists of values that +specify the dependencies of this particular task in relation to other tasks. + +The optional `nowait` attribute, when present, eliminates the implicit +barrier at the end of the construct, so the parent operation can make +progress even if the child operation has not completed yet. + +The optional `map_operands` maps data from the current task\'s data +environment to the device data environment. +""" +function target_exit_data( + if_expr=nothing::Union{Nothing,Value}; + device=nothing::Union{Nothing,Value}, + depend_vars::Vector{Value}, + map_operands::Vector{Value}, + depends=nothing, + nowait=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[depend_vars..., map_operands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(if_expr) && push!(_operands, if_expr) + !isnothing(device) && push!(_operands, device) + push!( + _attributes, + operandsegmentsizes([ + isnothing(if_expr) ? 0 : 1, + isnothing(device) ? 0 : 1, + length(depend_vars), + length(map_operands), + ]), + ) + !isnothing(depends) && push!(_attributes, namedattribute("depends", depends)) + !isnothing(nowait) && push!(_attributes, namedattribute("nowait", nowait)) + + return IR.create_operation( + "omp.target_exit_data", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`target` + +The target construct includes a region of code which is to be executed +on a device. + +The optional `if_expr` parameter specifies a boolean result of a +conditional check. If this value is 1 or is not provided then the target +region runs on a device, if it is 0 then the target region is executed on the +host device. + +The optional `device` parameter specifies the device number for the target +region. + +The optional `thread_limit` specifies the limit on the number of threads. + +The `depends` and `depend_vars` arguments are variadic lists of values that +specify the dependencies of this particular task in relation to other tasks. + +The optional `nowait` attribute, when present, eliminates the implicit +barrier at the end of the construct, so the parent operation can make +progress even if the child operation has not completed yet. + +The optional `is_device_ptr` indicates list items are device pointers. + +The optional `has_device_addr` indicates that list items already have device +addresses, so they may be directly accessed from the target device. This +includes array sections. + +The optional `map_operands` maps data from the current task\'s data +environment to the device data environment. +""" +function target( + if_expr=nothing::Union{Nothing,Value}; + device=nothing::Union{Nothing,Value}, + thread_limit=nothing::Union{Nothing,Value}, + depend_vars::Vector{Value}, + is_device_ptr::Vector{Value}, + has_device_addr::Vector{Value}, + map_operands::Vector{Value}, + private_vars::Vector{Value}, + depends=nothing, + nowait=nothing, + privatizers=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ + depend_vars..., + is_device_ptr..., + has_device_addr..., + map_operands..., + private_vars..., + ] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(if_expr) && push!(_operands, if_expr) + !isnothing(device) && push!(_operands, device) + !isnothing(thread_limit) && push!(_operands, thread_limit) + push!( + _attributes, + operandsegmentsizes([ + isnothing(if_expr) ? 0 : 1, + isnothing(device) ? 0 : 1, + isnothing(thread_limit) ? 0 : 1, + length(depend_vars), + length(is_device_ptr), + length(has_device_addr), + length(map_operands), + length(private_vars), + ]), + ) + !isnothing(depends) && push!(_attributes, namedattribute("depends", depends)) + !isnothing(nowait) && push!(_attributes, namedattribute("nowait", nowait)) + !isnothing(privatizers) && + push!(_attributes, namedattribute("privatizers", privatizers)) + + return IR.create_operation( + "omp.target", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`target_update` + +The target update directive makes the corresponding list items in the device +data environment consistent with their original list items, according to the +specified motion clauses. The target update construct is a stand-alone +directive. + +The optional `if_expr` parameter specifies a boolean result of a +conditional check. If this value is 1 or is not provided then the target +region runs on a device, if it is 0 then the target region is executed +on the host device. + +We use `MapInfoOp` to model the motion clauses and their modifiers. Even +though the spec differentiates between map-types & map-type-modifiers vs. +motion-clauses & motion-modifiers, the motion clauses and their modifiers +are a subset of map types and their modifiers. The subset relation is +handled in during verification to make sure the restrictions for target +update are respected. + +The optional `device` parameter specifies the device number for the target +region. + +The `depends` and `depend_vars` arguments are variadic lists of values that +specify the dependencies of this particular task in relation to other tasks. + +The optional `nowait` attribute, when present, eliminates the implicit +barrier at the end of the construct, so the parent operation can make +progress even if the child operation has not completed yet. + +The optional `map_operands` maps data from the current task\'s data +environment to the device data environment. +""" +function target_update( + if_expr=nothing::Union{Nothing,Value}; + device=nothing::Union{Nothing,Value}, + depend_vars::Vector{Value}, + map_operands::Vector{Value}, + depends=nothing, + nowait=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[depend_vars..., map_operands...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(if_expr) && push!(_operands, if_expr) + !isnothing(device) && push!(_operands, device) + push!( + _attributes, + operandsegmentsizes([ + isnothing(if_expr) ? 0 : 1, + isnothing(device) ? 0 : 1, + length(depend_vars), + length(map_operands), + ]), + ) + !isnothing(depends) && push!(_attributes, namedattribute("depends", depends)) + !isnothing(nowait) && push!(_attributes, namedattribute("nowait", nowait)) + + return IR.create_operation( + "omp.target_update", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`task` + +The task construct defines an explicit task. + +For definitions of \"undeferred task\", \"included task\", \"final task\" and +\"mergeable task\", please check OpenMP Specification. + +When an `if` clause is present on a task construct, and the value of +`if_expr` evaluates to `false`, an \"undeferred task\" is generated, and the +encountering thread must suspend the current task region, for which +execution cannot be resumed until execution of the structured block that is +associated with the generated task is completed. + +The `in_reduction` clause specifies that this particular task (among all the +tasks in current taskgroup, if any) participates in a reduction. +`in_reduction_vars_byref` indicates whether each reduction variable should +be passed by value or by reference. + +When a `final` clause is present and the `final` clause expression evaluates +to `true`, the generated tasks will be final tasks. All task constructs +encountered during execution of a final task will generate final and +included tasks. The use of a variable in a `final` clause expression causes +an implicit reference to the variable in all enclosing constructs. + +If the `untied` clause is present on a task construct, any thread in the +team can resume the task region after a suspension. The `untied` clause is +ignored if a `final` clause is present on the same task construct and the +`final_expr` evaluates to `true`, or if a task is an included task. + +When the `mergeable` clause is present, the tasks generated by the construct +are \"mergeable tasks\". + +The `priority` clause is a hint for the priority of the generated tasks. +The `priority` is a non-negative integer expression that provides a hint for +task execution order. Among all tasks ready to be executed, higher priority +tasks (those with a higher numerical value in the priority clause +expression) are recommended to execute before lower priority ones. The +default priority-value when no priority clause is specified should be +assumed to be zero (the lowest priority). + +The `depends` and `depend_vars` arguments are variadic lists of values that +specify the dependencies of this particular task in relation to other tasks. + +The `allocators_vars` and `allocate_vars` parameters are a variadic list of +values that specify the memory allocator to be used to obtain storage for +private values. +""" +function task( + if_expr=nothing::Union{Nothing,Value}; + final_expr=nothing::Union{Nothing,Value}, + in_reduction_vars::Vector{Value}, + priority=nothing::Union{Nothing,Value}, + depend_vars::Vector{Value}, + allocate_vars::Vector{Value}, + allocators_vars::Vector{Value}, + untied=nothing, + mergeable=nothing, + in_reduction_vars_byref=nothing, + in_reductions=nothing, + depends=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ + in_reduction_vars..., depend_vars..., allocate_vars..., allocators_vars... + ] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(if_expr) && push!(_operands, if_expr) + !isnothing(final_expr) && push!(_operands, final_expr) + !isnothing(priority) && push!(_operands, priority) + push!( + _attributes, + operandsegmentsizes([ + isnothing(if_expr) ? 0 : 1, + isnothing(final_expr) ? 0 : 1, + length(in_reduction_vars), + isnothing(priority) ? 0 : 1, + length(depend_vars), + length(allocate_vars), + length(allocators_vars), + ]), + ) + !isnothing(untied) && push!(_attributes, namedattribute("untied", untied)) + !isnothing(mergeable) && push!(_attributes, namedattribute("mergeable", mergeable)) + !isnothing(in_reduction_vars_byref) && push!( + _attributes, namedattribute("in_reduction_vars_byref", in_reduction_vars_byref) + ) + !isnothing(in_reductions) && + push!(_attributes, namedattribute("in_reductions", in_reductions)) + !isnothing(depends) && push!(_attributes, namedattribute("depends", depends)) + + return IR.create_operation( + "omp.task", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`taskgroup` + +The taskgroup construct specifies a wait on completion of child tasks of the +current task and their descendent tasks. + +When a thread encounters a taskgroup construct, it starts executing the +region. All child tasks generated in the taskgroup region and all of their +descendants that bind to the same parallel region as the taskgroup region +are part of the taskgroup set associated with the taskgroup region. There is +an implicit task scheduling point at the end of the taskgroup region. The +current task is suspended at the task scheduling point until all tasks in +the taskgroup set complete execution. + +The `task_reduction` clause specifies a reduction among tasks. For each list +item, the number of copies is unspecified. Any copies associated with the +reduction are initialized before they are accessed by the tasks +participating in the reduction. After the end of the region, the original +list item contains the result of the reduction. Similarly to the `reduction` +clause, accumulator variables must be passed in `task_reduction_vars`, +symbols referring to reduction declarations in the `task_reductions` +attribute, and whether the reduction variable should be passed into the +reduction region by value or by reference in `task_reduction_vars_byref`. + +The `allocators_vars` and `allocate_vars` parameters are a variadic list of +values that specify the memory allocator to be used to obtain storage for +private values. +""" +function taskgroup( + task_reduction_vars::Vector{Value}, + allocate_vars::Vector{Value}, + allocators_vars::Vector{Value}; + task_reduction_vars_byref=nothing, + task_reductions=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[task_reduction_vars..., allocate_vars..., allocators_vars...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!( + _attributes, + operandsegmentsizes([ + length(task_reduction_vars), length(allocate_vars), length(allocators_vars) + ]), + ) + !isnothing(task_reduction_vars_byref) && push!( + _attributes, + namedattribute("task_reduction_vars_byref", task_reduction_vars_byref), + ) + !isnothing(task_reductions) && + push!(_attributes, namedattribute("task_reductions", task_reductions)) + + return IR.create_operation( + "omp.taskgroup", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`taskloop` + +The taskloop construct specifies that the iterations of one or more +associated loops will be executed in parallel using explicit tasks. The +iterations are distributed across tasks generated by the construct and +scheduled to be executed. + +The body region can only contain a single block which must contain a single +operation and a terminator. The operation must be another compatible loop +wrapper or an `omp.loop_nest`. + +``` +omp.taskloop { + omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { + %a = load %arrA[%i1, %i2] : memref + %b = load %arrB[%i1, %i2] : memref + %sum = arith.addf %a, %b : f32 + store %sum, %arrC[%i1, %i2] : memref + omp.yield + } + omp.terminator +} +``` + +For definitions of \"undeferred task\", \"included task\", \"final task\" and +\"mergeable task\", please check OpenMP Specification. + +When an `if` clause is present on a taskloop construct, and if the `if` +clause expression evaluates to `false`, undeferred tasks are generated. The +use of a variable in an `if` clause expression of a taskloop construct +causes an implicit reference to the variable in all enclosing constructs. + +When a `final` clause is present and the `final` clause expression evaluates +to `true`, the generated tasks will be final tasks. All task constructs +encountered during execution of a final task will generate final and +included tasks. The use of a variable in a `final` clause expression causes +an implicit reference to the variable in all enclosing constructs. + +If the `untied` clause is present on a task construct, any thread in the +team can resume the task region after a suspension. The `untied` clause is +ignored if a `final` clause is present on the same task construct and the +`final_expr` evaluates to `true`, or if a task is an included task. + +When the `mergeable` clause is present, the tasks generated by the construct +are \"mergeable tasks\". + +Reductions can be performed by specifying reduction accumulator variables in +`reduction_vars`, symbols referring to reduction declarations in the +`reductions` attribute, and whether the reduction variable should be passed +into the reduction region by value or by reference in +`reduction_vars_byref`. Each reduction is identified by the accumulator it +uses and accumulators must not be repeated in the same reduction. A private +variable corresponding to the accumulator is used in place of the +accumulator inside the body of the operation. The reduction declaration +specifies how to combine the values from each iteration, section, team, +thread or simd lane defined by the operation\'s region into the final value, +which is available in the accumulator after they all complete. + +The `priority` clause is a hint for the priority of the generated tasks. +The `priority` is a non-negative integer expression that provides a hint for +task execution order. Among all tasks ready to be executed, higher priority +tasks (those with a higher numerical value in the priority clause +expression) are recommended to execute before lower priority ones. The +default priority-value when no priority clause is specified should be +assumed to be zero (the lowest priority). + +The `allocators_vars` and `allocate_vars` parameters are a variadic list of +values that specify the memory allocator to be used to obtain storage for +private values. + +If a `grainsize` clause is present, the number of logical loop iterations +assigned to each generated task is greater than or equal to the minimum of +the value of the grain-size expression and the number of logical loop +iterations, but less than two times the value of the grain-size expression. + +If `num_tasks` is specified, the taskloop construct creates as many tasks as +the minimum of the num-tasks expression and the number of logical loop +iterations. Each task must have at least one logical loop iteration. + +By default, the taskloop construct executes as if it was enclosed in a +taskgroup construct with no statements or directives outside of the taskloop +construct. Thus, the taskloop construct creates an implicit taskgroup +region. If the `nogroup` clause is present, no implicit taskgroup region is +created. + +If an `in_reduction` clause is present on the taskloop construct, the +behavior is as if each generated task was defined by a task construct on +which an `in_reduction` clause with the same reduction operator and list +items is present. Thus, the generated tasks are participants of a reduction +previously defined by a reduction scoping clause. In this case, accumulator +variables are specified in `in_reduction_vars`, symbols referring to +reduction declarations in `in_reductions` and `in_reduction_vars_byref` +indicate for each reduction variable whether it should be passed by value or +by reference. + +If a `reduction` clause is present on the taskloop construct, the behavior +is as if a `task_reduction` clause with the same reduction operator and list +items was applied to the implicit taskgroup construct enclosing the taskloop +construct. The taskloop construct executes as if each generated task was +defined by a task construct on which an `in_reduction` clause with the same +reduction operator and list items is present. Thus, the generated tasks are +participants of the reduction defined by the `task_reduction` clause that +was applied to the implicit taskgroup construct. +""" +function taskloop( + if_expr=nothing::Union{Nothing,Value}; + final_expr=nothing::Union{Nothing,Value}, + in_reduction_vars::Vector{Value}, + reduction_vars::Vector{Value}, + priority=nothing::Union{Nothing,Value}, + allocate_vars::Vector{Value}, + allocators_vars::Vector{Value}, + grain_size=nothing::Union{Nothing,Value}, + num_tasks=nothing::Union{Nothing,Value}, + untied=nothing, + mergeable=nothing, + in_reduction_vars_byref=nothing, + in_reductions=nothing, + reduction_vars_byref=nothing, + reductions=nothing, + nogroup=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[ + in_reduction_vars..., reduction_vars..., allocate_vars..., allocators_vars... + ] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(if_expr) && push!(_operands, if_expr) + !isnothing(final_expr) && push!(_operands, final_expr) + !isnothing(priority) && push!(_operands, priority) + !isnothing(grain_size) && push!(_operands, grain_size) + !isnothing(num_tasks) && push!(_operands, num_tasks) + push!( + _attributes, + operandsegmentsizes([ + isnothing(if_expr) ? 0 : 1, + isnothing(final_expr) ? 0 : 1, + length(in_reduction_vars), + length(reduction_vars), + isnothing(priority) ? 0 : 1, + length(allocate_vars), + length(allocators_vars), + isnothing(grain_size) ? 0 : 1, + isnothing(num_tasks) ? 0 : 1, + ]), + ) + !isnothing(untied) && push!(_attributes, namedattribute("untied", untied)) + !isnothing(mergeable) && push!(_attributes, namedattribute("mergeable", mergeable)) + !isnothing(in_reduction_vars_byref) && push!( + _attributes, namedattribute("in_reduction_vars_byref", in_reduction_vars_byref) + ) + !isnothing(in_reductions) && + push!(_attributes, namedattribute("in_reductions", in_reductions)) + !isnothing(reduction_vars_byref) && + push!(_attributes, namedattribute("reduction_vars_byref", reduction_vars_byref)) + !isnothing(reductions) && push!(_attributes, namedattribute("reductions", reductions)) + !isnothing(nogroup) && push!(_attributes, namedattribute("nogroup", nogroup)) + + return IR.create_operation( + "omp.taskloop", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`taskwait` + +The taskwait construct specifies a wait on the completion of child tasks +of the current task. +""" +function taskwait(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "omp.taskwait", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`taskyield` + +The taskyield construct specifies that the current task can be suspended +in favor of execution of a different task. +""" +function taskyield(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "omp.taskyield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`teams` + +The teams construct defines a region of code that triggers the creation of a +league of teams. Once created, the number of teams remains constant for the +duration of its code region. + +If the `if_expr` is present and it evaluates to `false`, the number of teams +created is one. + +The optional `num_teams_upper` and `num_teams_lower` arguments specify the +limit on the number of teams to be created. If only the upper bound is +specified, it acts as if the lower bound was set to the same value. It is +not allowed to set `num_teams_lower` if `num_teams_upper` is not specified. +They define a closed range, where both the lower and upper bounds are +included. + +The optional `thread_limit` specifies the limit on the number of threads. + +The `allocators_vars` and `allocate_vars` parameters are a variadic list of +values that specify the memory allocator to be used to obtain storage for +private values. + +Reductions can be performed by specifying reduction accumulator variables in +`reduction_vars`, symbols referring to reduction declarations in the +`reductions` attribute, and whether the reduction variable should be passed +into the reduction region by value or by reference in +`reduction_vars_byref`. Each reduction is identified by the accumulator it +uses and accumulators must not be repeated in the same reduction. A private +variable corresponding to the accumulator is used in place of the +accumulator inside the body of the operation. The reduction declaration +specifies how to combine the values from each iteration, section, team, +thread or simd lane defined by the operation\'s region into the final value, +which is available in the accumulator after they all complete. +""" +function teams( + num_teams_lower=nothing::Union{Nothing,Value}; + num_teams_upper=nothing::Union{Nothing,Value}, + if_expr=nothing::Union{Nothing,Value}, + thread_limit=nothing::Union{Nothing,Value}, + allocate_vars::Vector{Value}, + allocators_vars::Vector{Value}, + reduction_vars::Vector{Value}, + reduction_vars_byref=nothing, + reductions=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[allocate_vars..., allocators_vars..., reduction_vars...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(num_teams_lower) && push!(_operands, num_teams_lower) + !isnothing(num_teams_upper) && push!(_operands, num_teams_upper) + !isnothing(if_expr) && push!(_operands, if_expr) + !isnothing(thread_limit) && push!(_operands, thread_limit) + push!( + _attributes, + operandsegmentsizes([ + isnothing(num_teams_lower) ? 0 : 1, + isnothing(num_teams_upper) ? 0 : 1, + isnothing(if_expr) ? 0 : 1, + isnothing(thread_limit) ? 0 : 1, + length(allocate_vars), + length(allocators_vars), + length(reduction_vars), + ]), + ) + !isnothing(reduction_vars_byref) && + push!(_attributes, namedattribute("reduction_vars_byref", reduction_vars_byref)) + !isnothing(reductions) && push!(_attributes, namedattribute("reductions", reductions)) + + return IR.create_operation( + "omp.teams", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`terminator` + +A terminator operation for regions that appear in the body of OpenMP +operation. These regions are not expected to return any value so the +terminator takes no operands. The terminator op returns control to the +enclosing op. +""" +function terminator(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "omp.terminator", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`threadprivate` + +The threadprivate directive specifies that variables are replicated, with +each thread having its own copy. + +The current implementation uses the OpenMP runtime to provide thread-local +storage (TLS). Using the TLS feature of the LLVM IR will be supported in +future. + +This operation takes in the address of a symbol that represents the original +variable and returns the address of its TLS. All occurrences of +threadprivate variables in a parallel region should use the TLS returned by +this operation. + +The `sym_addr` refers to the address of the symbol, which is a pointer to +the original variable. +""" +function threadprivate(sym_addr::Value; tls_addr::IR.Type, location=Location()) + _results = IR.Type[tls_addr,] + _operands = Value[sym_addr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "omp.threadprivate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`wsloop` + +The worksharing-loop construct specifies that the iterations of the loop(s) +will be executed in parallel by threads in the current context. These +iterations are spread across threads that already exist in the enclosing +parallel region. + +The body region can only contain a single block which must contain a single +operation and a terminator. The operation must be another compatible loop +wrapper or an `omp.loop_nest`. + +``` +omp.wsloop { + omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) { + %a = load %arrA[%i1, %i2] : memref + %b = load %arrB[%i1, %i2] : memref + %sum = arith.addf %a, %b : f32 + store %sum, %arrC[%i1, %i2] : memref + omp.yield + } + omp.terminator +} +``` + +The `linear_step_vars` operand additionally specifies the step for each +associated linear operand. Note that the `linear_vars` and +`linear_step_vars` variadic lists should contain the same number of +elements. + +Reductions can be performed by specifying reduction accumulator variables in +`reduction_vars`, symbols referring to reduction declarations in the +`reductions` attribute, and whether the reduction variable should be passed +into the reduction region by value or by reference in +`reduction_vars_byref`. Each reduction is identified by the accumulator it +uses and accumulators must not be repeated in the same reduction. A private +variable corresponding to the accumulator is used in place of the +accumulator inside the body of the operation. The reduction declaration +specifies how to combine the values from each iteration, section, team, +thread or simd lane defined by the operation\'s region into the final value, +which is available in the accumulator after they all complete. + +The optional `schedule_val` attribute specifies the loop schedule for this +loop, determining how the loop is distributed across the parallel threads. +The optional `schedule_chunk_var` associated with this determines further +controls this distribution. + +The optional `nowait` attribute, when present, eliminates the implicit +barrier at the end of the construct, so the parent operation can make +progress even if the child operation has not completed yet. + +The optional `ordered_val` attribute specifies how many loops are associated +with the worksharing-loop construct. The value of zero refers to the ordered +clause specified without parameter. + +The optional `order` attribute specifies which order the iterations of the +associated loops are executed in. Currently the only option for this +attribute is \"concurrent\". +""" +function wsloop( + linear_vars::Vector{Value}, + linear_step_vars::Vector{Value}, + reduction_vars::Vector{Value}, + schedule_chunk_var=nothing::Union{Nothing,Value}; + reduction_vars_byref=nothing, + reductions=nothing, + schedule_val=nothing, + schedule_modifier=nothing, + simd_modifier=nothing, + nowait=nothing, + ordered_val=nothing, + order_val=nothing, + order_mod=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[linear_vars..., linear_step_vars..., reduction_vars...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(schedule_chunk_var) && push!(_operands, schedule_chunk_var) + push!( + _attributes, + operandsegmentsizes([ + length(linear_vars), + length(linear_step_vars), + length(reduction_vars), + isnothing(schedule_chunk_var) ? 0 : 1, + ]), + ) + !isnothing(reduction_vars_byref) && + push!(_attributes, namedattribute("reduction_vars_byref", reduction_vars_byref)) + !isnothing(reductions) && push!(_attributes, namedattribute("reductions", reductions)) + !isnothing(schedule_val) && + push!(_attributes, namedattribute("schedule_val", schedule_val)) + !isnothing(schedule_modifier) && + push!(_attributes, namedattribute("schedule_modifier", schedule_modifier)) + !isnothing(simd_modifier) && + push!(_attributes, namedattribute("simd_modifier", simd_modifier)) + !isnothing(nowait) && push!(_attributes, namedattribute("nowait", nowait)) + !isnothing(ordered_val) && + push!(_attributes, namedattribute("ordered_val", ordered_val)) + !isnothing(order_val) && push!(_attributes, namedattribute("order_val", order_val)) + !isnothing(order_mod) && push!(_attributes, namedattribute("order_mod", order_mod)) + + return IR.create_operation( + "omp.wsloop", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +\"omp.yield\" yields SSA values from the OpenMP dialect op region and +terminates the region. The semantics of how the values are yielded is +defined by the parent operation. +""" +function yield(results::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[results...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "omp.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # omp diff --git a/src/Dialects/19/PDL.jl b/src/Dialects/19/PDL.jl new file mode 100644 index 00000000..d5e58d7a --- /dev/null +++ b/src/Dialects/19/PDL.jl @@ -0,0 +1,777 @@ +module pdl + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`apply_native_constraint` + +`pdl.apply_native_constraint` operations apply a native C++ constraint, that +has been registered externally with the consumer of PDL, to a given set of +entities and optionally return a number of values. + +# Example + +```mlir +// Apply `myConstraint` to the entities defined by `input`, `attr`, and `op`. +pdl.apply_native_constraint \"myConstraint\"(%input, %attr, %op : !pdl.value, !pdl.attribute, !pdl.operation) +// Apply constraint `with_result` to `root`. This constraint returns an attribute. +%attr = pdl.apply_native_constraint \"with_result\"(%root : !pdl.operation) : !pdl.attribute +``` +""" +function apply_native_constraint( + args::Vector{Value}; + results::Vector{IR.Type}, + name, + isNegated=nothing, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("name", name),] + !isnothing(isNegated) && push!(_attributes, namedattribute("isNegated", isNegated)) + + return IR.create_operation( + "pdl.apply_native_constraint", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_native_rewrite` + +`pdl.apply_native_rewrite` operations apply a native C++ function, that has +been registered externally with the consumer of PDL, to perform a rewrite +and optionally return a number of values. The native function may accept any +number of arguments. This operation is used within a pdl.rewrite region to enable +the interleaving of native rewrite methods with other pdl constructs. + +# Example + +```mlir +// Apply a native rewrite method that returns an attribute. +%ret = pdl.apply_native_rewrite \"myNativeFunc\"(%arg0, %attr1) : !pdl.attribute +``` + +```c++ +// The native rewrite as defined in C++: +static Attribute myNativeFunc(PatternRewriter &rewriter, Value arg0, Attribute arg1) { + // Just return the second arg. + return arg1; +} + +void registerNativeRewrite(PDLPatternModule &pdlModule) { + pdlModule.registerRewriteFunction(\"myNativeFunc\", myNativeFunc); +} +``` +""" +function apply_native_rewrite( + args::Vector{Value}; results::Vector{IR.Type}, name, location=Location() +) + _results = IR.Type[results...,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("name", name),] + + return IR.create_operation( + "pdl.apply_native_rewrite", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`attribute` + +`pdl.attribute` operations capture named attribute edges into an operation. +Instances of this operation define, and partially constrain, attributes of a +given operation. A `pdl.attribute` may partially constrain the input by +specifying an expected attribute value type (via a `pdl.type` operation), or +a constant value for the attribute (via `val`). Only one of these may be set +for a given input, as the type of the constant value provides the type. When +defined within a `pdl.rewrite` region, the constant value must be specified. + +# Example + +```mlir +// Define an attribute: +%attr = pdl.attribute + +// Define an attribute with an expected type: +%type = pdl.type : i32 +%attr = pdl.attribute : %type + +// Define an attribute with a constant value: +%attr = pdl.attribute = \"hello\" +``` +""" +function attribute( + valueType=nothing::Union{Nothing,Value}; + attr::IR.Type, + value=nothing, + location=Location(), +) + _results = IR.Type[attr,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(valueType) && push!(_operands, valueType) + !isnothing(value) && push!(_attributes, namedattribute("value", value)) + + return IR.create_operation( + "pdl.attribute", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`erase` + +`pdl.erase` operations are used within `pdl.rewrite` regions to specify that +an input operation should be marked as erased. The semantics of this +operation correspond with the `eraseOp` method on a `PatternRewriter`. + +# Example + +```mlir +pdl.erase %root +``` +""" +function erase(opValue::Value; location=Location()) + _results = IR.Type[] + _operands = Value[opValue,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl.erase", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`operand` + +`pdl.operand` operations capture external operand edges into an operation +node that originate from operations or block arguments not otherwise +specified within the pattern (i.e. via `pdl.result` or `pdl.results`). These +operations define individual operands of a given operation. A `pdl.operand` +may partially constrain an operand by specifying an expected value type +(via a `pdl.type` operation). + +# Example + +```mlir +// Define an external operand: +%operand = pdl.operand + +// Define an external operand with an expected type: +%type = pdl.type : i32 +%operand = pdl.operand : %type +``` +""" +function operand( + valueType=nothing::Union{Nothing,Value}; value::IR.Type, location=Location() +) + _results = IR.Type[value,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(valueType) && push!(_operands, valueType) + + return IR.create_operation( + "pdl.operand", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`operands` + +`pdl.operands` operations capture external operand range edges into an +operation node that originate from operations or block arguments not +otherwise specified within the pattern (i.e. via `pdl.result` or +`pdl.results`). These operations define groups of input operands into a +given operation. A `pdl.operands` may partially constrain a set of input +operands by specifying expected value types (via `pdl.types` operations). + +# Example + +```mlir +// Define a range of input operands: +%operands = pdl.operands + +// Define a range of input operands with expected types: +%types = pdl.types : [i32, i64, i32] +%typed_operands = pdl.operands : %types +``` +""" +function operands( + valueType=nothing::Union{Nothing,Value}; value::IR.Type, location=Location() +) + _results = IR.Type[value,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(valueType) && push!(_operands, valueType) + + return IR.create_operation( + "pdl.operands", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`operation` + +`pdl.operation` operations define operation nodes within a pattern. Within +a match sequence, i.e. when directly nested within a `pdl.pattern`, these +operations correspond to input operations, or those that already existing +within the MLIR module. Inside of a `pdl.rewrite`, these operations +correspond to operations that should be created as part of the replacement +sequence. + +`pdl.operation`s are composed of a name, and a set of attribute, operand, +and result type values, that map to what those that would be on a +constructed instance of that operation. The results of a `pdl.operation` are +a handle to the operation itself. Handles to the results of the operation +can be extracted via `pdl.result`. + +# Example + +```mlir +// Define an instance of a `foo.op` operation. +%op = pdl.operation \"foo.op\"(%arg0, %arg1 : !pdl.value, !pdl.value) + {\"attrA\" = %attr0} -> (%type, %type : !pdl.type, !pdl.type) +``` + +When used within a matching context, the name of the operation may be +omitted. + +When used within a rewriting context, i.e. when defined within a +`pdl.rewrite`, all of the result types must be \"inferable\". This means that +the type must be attributable to either a constant type value or the result +type of another entity, such as an attribute, the result of a +`apply_native_rewrite`, or the result type of another operation. If the +result type value does not meet any of these criteria, the operation must +override the `InferTypeOpInterface` to ensure that the result types can be +inferred. + +The operands of the operation are interpreted in the following ways: + +1) A single !pdl.range: + +In this case, the single range is treated as all of the operands of the +operation. + +```mlir +// Define an instance with single range of operands. +%op = pdl.operation \"func.return\"(%allArgs : !pdl.range) +``` + +2) A variadic number of either !pdl.value or !pdl.range: + +In this case, the inputs are expected to correspond with the operand groups +defined on the operation in ODS. + +```tablgen +// Given the following operation definition in ODS: +def MyIndirectCallOp { + let results = (outs FunctionType:\$call, Variadic:\$args); +} +``` + +```mlir +// We can match the operands as so: +%op = pdl.operation \"my.indirect_call\"(%call, %args : !pdl.value, !pdl.range) +``` + +The results of the operation are interpreted in the following ways: + +1) A single !pdl.range: + +In this case, the single range is treated as all of the result types of the +operation. + +```mlir +// Define an instance with single range of types. +%allResultTypes = pdl.types +%op = pdl.operation \"builtin.unrealized_conversion_cast\" -> (%allResultTypes : !pdl.types) +``` + +2) A variadic number of either !pdl.type or !pdl.range: + +In this case, the inputs are expected to correspond with the result groups +defined on the operation in ODS. + +```tablgen +// Given the following operation definition in ODS: +def MyOp { + let results = (outs SomeType:\$result, Variadic:\$otherResults); +} +``` + +```mlir +// We can match the results as so: +%result = pdl.type +%otherResults = pdl.types +%op = pdl.operation \"foo.op\" -> (%result, %otherResults : !pdl.type, !pdl.range) +``` +""" +function operation( + operandValues::Vector{Value}, + attributeValues::Vector{Value}, + typeValues::Vector{Value}; + op::IR.Type, + opName=nothing, + attributeValueNames, + location=Location(), +) + _results = IR.Type[op,] + _operands = Value[operandValues..., attributeValues..., typeValues...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("attributeValueNames", attributeValueNames),] + push!( + _attributes, + operandsegmentsizes([ + length(operandValues), length(attributeValues), length(typeValues) + ]), + ) + !isnothing(opName) && push!(_attributes, namedattribute("opName", opName)) + + return IR.create_operation( + "pdl.operation", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pattern` + +`pdl.pattern` operations provide a transformable representation for a +`RewritePattern`. The attributes on this operation correspond to the various +metadata on a `RewritePattern`, such as the benefit. The match section of +the pattern is specified within the region body, with the rewrite provided +by a terminating `pdl.rewrite`. + +# Example + +```mlir +// Provide a pattern matching \"foo.op\" that replaces the root with its +// operand. +pdl.pattern : benefit(1) { + %resultType = pdl.type + %inputOperand = pdl.operand + %root = pdl.operation \"foo.op\"(%inputOperand) -> (%resultType) + pdl.rewrite %root { + pdl.replace %root with (%inputOperand) + } +} +``` +""" +function pattern(; benefit, sym_name=nothing, bodyRegion::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[bodyRegion,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("benefit", benefit),] + !isnothing(sym_name) && push!(_attributes, namedattribute("sym_name", sym_name)) + + return IR.create_operation( + "pdl.pattern", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`range` + +`pdl.range` operations construct a range from a given set of PDL entities, +which all share the same underlying element type. For example, a +`!pdl.range` may be constructed from a list of `!pdl.value` +or `!pdl.range` entities. + +# Example + +```mlir +// Construct a range of values. +%valueRange = pdl.range %inputValue, %inputRange : !pdl.value, !pdl.range + +// Construct a range of types. +%typeRange = pdl.range %inputType, %inputRange : !pdl.type, !pdl.range + +// Construct an empty range of types. +%valueRange = pdl.range : !pdl.range +``` + +TODO: Range construction is currently limited to rewrites, but it could +be extended to constraints under certain circustances; i.e., if we can +determine how to extract the underlying elements. If we can\'t, e.g. if +there are multiple sub ranges used for construction, we won\'t be able +to determine their sizes during constraint time. +""" +function range(arguments::Vector{Value}; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[arguments...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl.range", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`replace` + +`pdl.replace` operations are used within `pdl.rewrite` regions to specify +that an input operation should be marked as replaced. The semantics of this +operation correspond with the `replaceOp` method on a `PatternRewriter`. The +set of replacement values can be either: +* a single `Operation` (`replOperation` should be populated) + - The operation will be replaced with the results of this operation. +* a set of `Value`s (`replValues` should be populated) + - The operation will be replaced with these values. + +# Example + +```mlir +// Replace root node with 2 values: +pdl.replace %root with (%val0, %val1 : !pdl.value, !pdl.value) + +// Replace root node with a range of values: +pdl.replace %root with (%vals : !pdl.range) + +// Replace root with another operation: +pdl.replace %root with %otherOp +``` +""" +function replace( + opValue::Value, + replOperation=nothing::Union{Nothing,Value}; + replValues::Vector{Value}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[opValue, replValues...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(replOperation) && push!(_operands, replOperation) + push!( + _attributes, + operandsegmentsizes([1, isnothing(replOperation) ? 0 : 1, length(replValues)]), + ) + + return IR.create_operation( + "pdl.replace", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`result` + +`pdl.result` operations extract result edges from an operation node within +a pattern or rewrite region. The provided index is zero-based, and +represents the concrete result to extract, i.e. this is not the result index +as defined by the ODS definition of the operation. + +# Example + +```mlir +// Extract a result: +%operation = pdl.operation ... +%pdl_result = pdl.result 1 of %operation + +// Imagine the following IR being matched: +%result_0, %result_1 = foo.op ... + +// If the example pattern snippet above were matching against `foo.op` in +// the IR snippet, `%pdl_result` would correspond to `%result_1`. +``` +""" +function result(parent::Value; val::IR.Type, index, location=Location()) + _results = IR.Type[val,] + _operands = Value[parent,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("index", index),] + + return IR.create_operation( + "pdl.result", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`results` + +`pdl.results` operations extract a result group from an operation within a +pattern or rewrite region. If an index is provided, this operation extracts +a result group as defined by the ODS definition of the operation. In this +case the result of this operation may be either a single `pdl.value` or +a `pdl.range`, depending on the constraint of the result in ODS. If +no index is provided, this operation extracts the full result range of the +operation. + +# Example + +```mlir +// Extract all of the results of an operation: +%operation = pdl.operation ... +%results = pdl.results of %operation + +// Extract the results in the first result group of an operation, which is +// variadic: +%operation = pdl.operation ... +%results = pdl.results 0 of %operation -> !pdl.range + +// Extract the results in the second result group of an operation, which is +// not variadic: +%operation = pdl.operation ... +%results = pdl.results 1 of %operation -> !pdl.value +``` +""" +function results(parent::Value; val::IR.Type, index=nothing, location=Location()) + _results = IR.Type[val,] + _operands = Value[parent,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(index) && push!(_attributes, namedattribute("index", index)) + + return IR.create_operation( + "pdl.results", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`rewrite` + +`pdl.rewrite` operations terminate the region of a `pdl.pattern` and specify +the main rewrite of a `pdl.pattern`, on the optional root operation. The +rewrite is specified either via a string name (`name`) to a native +rewrite function, or via the region body. The rewrite region, if specified, +must contain a single block. If the rewrite is external it functions +similarly to `pdl.apply_native_rewrite`, and takes a set of additional +positional values defined within the matcher as arguments. If the rewrite is +external, the root operation is passed to the native function as the leading +arguments. The root operation, if provided, specifies the starting point in +the pattern for the subgraph isomorphism search. Pattern matching will proceed +from this node downward (towards the defining operation) or upward +(towards the users) until all the operations in the pattern have been matched. +If the root is omitted, the pdl_interp lowering will automatically select +the best root of the pdl.rewrite among all the operations in the pattern. + +# Example + +```mlir +// Specify an external rewrite function: +pdl.rewrite %root with \"myExternalRewriter\"(%value : !pdl.value) + +// Specify a rewrite inline using PDL with the given root: +pdl.rewrite %root { + %op = pdl.operation \"foo.op\"(%arg0, %arg1) + pdl.replace %root with %op +} + +// Specify a rewrite inline using PDL, automatically selecting root: +pdl.rewrite { + %op1 = pdl.operation \"foo.op\"(%arg0, %arg1) + %op2 = pdl.operation \"bar.op\"(%arg0, %arg1) + pdl.replace %root1 with %op1 + pdl.replace %root2 with %op2 +} +``` +""" +function rewrite( + root=nothing::Union{Nothing,Value}; + externalArgs::Vector{Value}, + name=nothing, + bodyRegion::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[externalArgs...,] + _owned_regions = Region[bodyRegion,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(root) && push!(_operands, root) + push!(_attributes, operandsegmentsizes([isnothing(root) ? 0 : 1, length(externalArgs)])) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + + return IR.create_operation( + "pdl.rewrite", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`type` + +`pdl.type` operations capture result type constraints of `Attributes`, +`Values`, and `Operations`. Instances of this operation define, and +partially constrain, results types of a given entity. A `pdl.type` may +partially constrain the result by specifying a constant `Type`. + +# Example + +```mlir +// Define a type: +%type = pdl.type + +// Define a type with a constant value: +%type = pdl.type : i32 +``` +""" +function type(; result::IR.Type, constantType=nothing, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(constantType) && + push!(_attributes, namedattribute("constantType", constantType)) + + return IR.create_operation( + "pdl.type", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`types` + +`pdl.types` operations capture result type constraints of `Value`s, and +`Operation`s. Instances of this operation define results types of a given +entity. A `pdl.types` may partially constrain the results by specifying +an array of `Type`s. + +# Example + +```mlir +// Define a range of types: +%types = pdl.types + +// Define a range of types with a range of constant values: +%types = pdl.types : [i32, i64, i32] +``` +""" +function types(; result::IR.Type, constantTypes=nothing, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(constantTypes) && + push!(_attributes, namedattribute("constantTypes", constantTypes)) + + return IR.create_operation( + "pdl.types", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # pdl diff --git a/src/Dialects/19/PDLInterp.jl b/src/Dialects/19/PDLInterp.jl new file mode 100644 index 00000000..06b02cf9 --- /dev/null +++ b/src/Dialects/19/PDLInterp.jl @@ -0,0 +1,1456 @@ +module pdl_interp + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`apply_constraint` + +`pdl_interp.apply_constraint` operations apply a generic constraint, that +has been registered with the interpreter, with a given set of positional +values. +The constraint function may return any number of results. +On success, this operation branches to the true destination, +otherwise the false destination is taken. This behavior can be reversed +by setting the attribute `isNegated` to true. + +# Example + +```mlir +// Apply `myConstraint` to the entities defined by `input`, `attr`, and +// `op`. +pdl_interp.apply_constraint \"myConstraint\"(%input, %attr, %op : !pdl.value, !pdl.attribute, !pdl.operation) -> ^matchDest, ^failureDest +``` +""" +function apply_constraint( + args::Vector{Value}; + results::Vector{IR.Type}, + name, + isNegated=nothing, + trueDest::Block, + falseDest::Block, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[trueDest, falseDest] + _attributes = NamedAttribute[namedattribute("name", name),] + !isnothing(isNegated) && push!(_attributes, namedattribute("isNegated", isNegated)) + + return IR.create_operation( + "pdl_interp.apply_constraint", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_rewrite` + +`pdl_interp.apply_rewrite` operations invoke an external rewriter that has +been registered with the interpreter to perform the rewrite after a +successful match. The rewrite is passed a set of positional arguments. The +rewrite function may return any number of results. + +# Example + +```mlir +// Rewriter operating solely on the root operation. +pdl_interp.apply_rewrite \"rewriter\"(%root : !pdl.operation) + +// Rewriter operating solely on the root operation and return an attribute. +%attr = pdl_interp.apply_rewrite \"rewriter\"(%root : !pdl.operation) : !pdl.attribute + +// Rewriter operating on the root operation along with additional arguments +// from the matcher. +pdl_interp.apply_rewrite \"rewriter\"(%root : !pdl.operation, %value : !pdl.value) +``` +""" +function apply_rewrite( + args::Vector{Value}; results::Vector{IR.Type}, name, location=Location() +) + _results = IR.Type[results...,] + _operands = Value[args...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("name", name),] + + return IR.create_operation( + "pdl_interp.apply_rewrite", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`are_equal` + +`pdl_interp.are_equal` operations compare two positional values for +equality. On success, this operation branches to the true destination, +otherwise the false destination is taken. + +# Example + +```mlir +pdl_interp.are_equal %result1, %result2 : !pdl.value -> ^matchDest, ^failureDest +``` +""" +function are_equal( + lhs::Value, rhs::Value; trueDest::Block, falseDest::Block, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[trueDest, falseDest] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.are_equal", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`branch` + +`pdl_interp.branch` operations expose general branch functionality to the +interpreter, and are generally used to branch from one pattern match +sequence to another. + +# Example + +```mlir +pdl_interp.branch ^dest +``` +""" +function branch(; dest::Block, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[dest,] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.branch", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`check_attribute` + +`pdl_interp.check_attribute` operations compare the value of a given +attribute with a constant value. On success, this operation branches to the +true destination, otherwise the false destination is taken. + +# Example + +```mlir +pdl_interp.check_attribute %attr is 10 -> ^matchDest, ^failureDest +``` +""" +function check_attribute( + attribute::Value; constantValue, trueDest::Block, falseDest::Block, location=Location() +) + _results = IR.Type[] + _operands = Value[attribute,] + _owned_regions = Region[] + _successors = Block[trueDest, falseDest] + _attributes = NamedAttribute[namedattribute("constantValue", constantValue),] + + return IR.create_operation( + "pdl_interp.check_attribute", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`check_operand_count` + +`pdl_interp.check_operand_count` operations compare the number of operands +of a given operation value with a constant. The comparison is either exact +or at_least, with the latter used to compare against a minimum number of +expected operands. On success, this operation branches to the true +destination, otherwise the false destination is taken. + +# Example + +```mlir +// Check for exact equality. +pdl_interp.check_operand_count of %op is 2 -> ^matchDest, ^failureDest + +// Check for at least N operands. +pdl_interp.check_operand_count of %op is at_least 2 -> ^matchDest, ^failureDest +``` +""" +function check_operand_count( + inputOp::Value; + count, + compareAtLeast=nothing, + trueDest::Block, + falseDest::Block, + location=Location(), +) + _results = IR.Type[] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[trueDest, falseDest] + _attributes = NamedAttribute[namedattribute("count", count),] + !isnothing(compareAtLeast) && + push!(_attributes, namedattribute("compareAtLeast", compareAtLeast)) + + return IR.create_operation( + "pdl_interp.check_operand_count", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`check_operation_name` + +`pdl_interp.check_operation_name` operations compare the name of a given +operation with a known name. On success, this operation branches to the true +destination, otherwise the false destination is taken. + +# Example + +```mlir +pdl_interp.check_operation_name of %op is \"foo.op\" -> ^matchDest, ^failureDest +``` +""" +function check_operation_name( + inputOp::Value; name, trueDest::Block, falseDest::Block, location=Location() +) + _results = IR.Type[] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[trueDest, falseDest] + _attributes = NamedAttribute[namedattribute("name", name),] + + return IR.create_operation( + "pdl_interp.check_operation_name", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`check_result_count` + +`pdl_interp.check_result_count` operations compare the number of results +of a given operation value with a constant. The comparison is either exact +or at_least, with the latter used to compare against a minimum number of +expected results. On success, this operation branches to the true +destination, otherwise the false destination is taken. + +# Example + +```mlir +// Check for exact equality. +pdl_interp.check_result_count of %op is 2 -> ^matchDest, ^failureDest + +// Check for at least N results. +pdl_interp.check_result_count of %op is at_least 2 -> ^matchDest, ^failureDest +``` +""" +function check_result_count( + inputOp::Value; + count, + compareAtLeast=nothing, + trueDest::Block, + falseDest::Block, + location=Location(), +) + _results = IR.Type[] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[trueDest, falseDest] + _attributes = NamedAttribute[namedattribute("count", count),] + !isnothing(compareAtLeast) && + push!(_attributes, namedattribute("compareAtLeast", compareAtLeast)) + + return IR.create_operation( + "pdl_interp.check_result_count", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`check_type` + +`pdl_interp.check_type` operations compare a type with a statically known +type. On success, this operation branches to the true destination, otherwise +the false destination is taken. + +# Example + +```mlir +pdl_interp.check_type %type is i32 -> ^matchDest, ^failureDest +``` +""" +function check_type( + value::Value; type, trueDest::Block, falseDest::Block, location=Location() +) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[trueDest, falseDest] + _attributes = NamedAttribute[namedattribute("type", type),] + + return IR.create_operation( + "pdl_interp.check_type", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`check_types` + +`pdl_interp.check_types` operations compare a range of types with a +statically known range of types. On success, this operation branches +to the true destination, otherwise the false destination is taken. + +# Example + +```mlir +pdl_interp.check_types %type are [i32, i64] -> ^matchDest, ^failureDest +``` +""" +function check_types( + value::Value; types, trueDest::Block, falseDest::Block, location=Location() +) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[trueDest, falseDest] + _attributes = NamedAttribute[namedattribute("types", types),] + + return IR.create_operation( + "pdl_interp.check_types", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`continue_` + +`pdl_interp.continue` operation breaks the current iteration within the +`pdl_interp.foreach` region and continues with the next iteration from +the beginning of the region. + +# Example + +```mlir +pdl_interp.continue +``` +""" +function continue_(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.continue", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_attribute` + +`pdl_interp.create_attribute` operations generate a handle within the +interpreter for a specific constant attribute value. + +# Example + +```mlir +%attr = pdl_interp.create_attribute 10 : i64 +``` +""" +function create_attribute(; attribute::IR.Type, value, location=Location()) + _results = IR.Type[attribute,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "pdl_interp.create_attribute", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_operation` + +`pdl_interp.create_operation` operations create an `Operation` instance with +the specified attributes, operands, and result types. See `pdl.operation` +for a more detailed description on the general interpretation of the arguments +to this operation. + +# Example + +```mlir +// Create an instance of a `foo.op` operation. +%op = pdl_interp.create_operation \"foo.op\"(%arg0 : !pdl.value) {\"attrA\" = %attr0} -> (%type : !pdl.type) + +// Create an instance of a `foo.op` operation that has inferred result types +// (using the InferTypeOpInterface). +%op = pdl_interp.create_operation \"foo.op\"(%arg0 : !pdl.value) {\"attrA\" = %attr0} -> +``` +""" +function create_operation( + inputOperands::Vector{Value}, + inputAttributes::Vector{Value}, + inputResultTypes::Vector{Value}; + resultOp::IR.Type, + name, + inputAttributeNames, + inferredResultTypes=nothing, + location=Location(), +) + _results = IR.Type[resultOp,] + _operands = Value[inputOperands..., inputAttributes..., inputResultTypes...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("name", name), + namedattribute("inputAttributeNames", inputAttributeNames), + ] + push!( + _attributes, + operandsegmentsizes([ + length(inputOperands), length(inputAttributes), length(inputResultTypes) + ]), + ) + !isnothing(inferredResultTypes) && + push!(_attributes, namedattribute("inferredResultTypes", inferredResultTypes)) + + return IR.create_operation( + "pdl_interp.create_operation", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_range` + +`pdl_interp.create_range` operations construct a range from a given set of PDL +entities, which all share the same underlying element type. For example, a +`!pdl.range` may be constructed from a list of `!pdl.value` +or `!pdl.range` entities. + +# Example + +```mlir +// Construct a range of values. +%valueRange = pdl_interp.create_range %inputValue, %inputRange : !pdl.value, !pdl.range + +// Construct a range of types. +%typeRange = pdl_interp.create_range %inputType, %inputRange : !pdl.type, !pdl.range + +// Construct an empty range of types. +%valueRange = pdl_interp.create_range : !pdl.range +``` +""" +function create_range(arguments::Vector{Value}; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[arguments...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.create_range", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_type` + +`pdl_interp.create_type` operations generate a handle within the interpreter +for a specific constant type value. + +# Example + +```mlir +pdl_interp.create_type i64 +``` +""" +function create_type(; result::IR.Type, value, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "pdl_interp.create_type", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_types` + +`pdl_interp.create_types` operations generate a handle within the +interpreter for a specific range of constant type values. + +# Example + +```mlir +pdl_interp.create_types [i64, i64] +``` +""" +function create_types(; result::IR.Type, value, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "pdl_interp.create_types", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`erase` + +`pdl.erase` operations are used to specify that an operation should be +marked as erased. The semantics of this operation correspond with the +`eraseOp` method on a `PatternRewriter`. + +# Example + +```mlir +pdl_interp.erase %root +``` +""" +function erase(inputOp::Value; location=Location()) + _results = IR.Type[] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.erase", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`extract` + +`pdl_interp.extract` operations are used to extract an item from a range +at the specified index. If the index is out of range, returns null. + +# Example + +```mlir +// Extract the value at index 1 from a range of values. +%ops = pdl_interp.extract 1 of %values : !pdl.value +``` +""" +function extract(range::Value; result::IR.Type, index, location=Location()) + _results = IR.Type[result,] + _operands = Value[range,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("index", index),] + + return IR.create_operation( + "pdl_interp.extract", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`finalize` + +`pdl_interp.finalize` is used to denote the termination of a match or +rewrite sequence. + +# Example + +```mlir +pdl_interp.finalize +``` +""" +function finalize(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.finalize", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`foreach` + +`pdl_interp.foreach` iteratively selects an element from a range of values +and executes the region until pdl.continue is reached. + +In the bytecode interpreter, this operation is implemented by looping over +the values and, for each selection, running the bytecode until we reach +pdl.continue. This may result in multiple matches being reported. Note +that the input range is mutated (popped from). + +# Example + +```mlir +pdl_interp.foreach %op : !pdl.operation in %ops { + pdl_interp.continue +} -> ^next +``` +""" +function foreach(values::Value; region::Region, successor::Block, location=Location()) + _results = IR.Type[] + _operands = Value[values,] + _owned_regions = Region[region,] + _successors = Block[successor,] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.foreach", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`func` + +`pdl_interp.func` operations act as interpreter functions. These are +callable SSA-region operations that contain other interpreter operations. +Interpreter functions are used for both the matching and the rewriting +portion of the interpreter. + +# Example + +```mlir +pdl_interp.func @rewriter(%root: !pdl.operation) { + %op = pdl_interp.create_operation \"foo.new_operation\" + pdl_interp.erase %root + pdl_interp.finalize +} +``` +""" +function func(; + sym_name, + function_type, + arg_attrs=nothing, + res_attrs=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("function_type", function_type) + ] + !isnothing(arg_attrs) && push!(_attributes, namedattribute("arg_attrs", arg_attrs)) + !isnothing(res_attrs) && push!(_attributes, namedattribute("res_attrs", res_attrs)) + + return IR.create_operation( + "pdl_interp.func", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_attribute` + +`pdl_interp.get_attribute` operations try to get a specific attribute from +an operation. If the operation does not have that attribute, a null value is +returned. + +# Example + +```mlir +%attr = pdl_interp.get_attribute \"attr\" of %op +``` +""" +function get_attribute(inputOp::Value; attribute::IR.Type, name, location=Location()) + _results = IR.Type[attribute,] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("name", name),] + + return IR.create_operation( + "pdl_interp.get_attribute", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_attribute_type` + +`pdl_interp.get_attribute_type` operations get the resulting type of a +specific attribute. + +# Example + +```mlir +%type = pdl_interp.get_attribute_type of %attr +``` +""" +function get_attribute_type(value::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.get_attribute_type", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_defining_op` + +`pdl_interp.get_defining_op` operations try to get the defining operation +of a specific value or range of values. In the case of range, the defining +op of the first value is returned. If the value is not an operation result +or range of operand results, null is returned. + +# Example + +```mlir +%op = pdl_interp.get_defining_op of %value : !pdl.value +``` +""" +function get_defining_op(value::Value; inputOp::IR.Type, location=Location()) + _results = IR.Type[inputOp,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.get_defining_op", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_operand` + +`pdl_interp.get_operand` operations try to get a specific operand from an +operation If the operation does not have an operand for the given index, a +null value is returned. + +# Example + +```mlir +%operand = pdl_interp.get_operand 1 of %op +``` +""" +function get_operand(inputOp::Value; value::IR.Type, index, location=Location()) + _results = IR.Type[value,] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("index", index),] + + return IR.create_operation( + "pdl_interp.get_operand", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_operands` + +`pdl_interp.get_operands` operations try to get a specific operand +group from an operation. If the expected result is a single Value, null is +returned if the operand group is not of size 1. If a range is expected, +null is returned if the operand group is invalid. If no index is provided, +the returned operand group corresponds to all operands of the operation. + +# Example + +```mlir +// Get the first group of operands from an operation, and expect a single +// element. +%operand = pdl_interp.get_operands 0 of %op : !pdl.value + +// Get the first group of operands from an operation. +%operands = pdl_interp.get_operands 0 of %op : !pdl.range + +// Get all of the operands from an operation. +%operands = pdl_interp.get_operands of %op : !pdl.range +``` +""" +function get_operands(inputOp::Value; value::IR.Type, index=nothing, location=Location()) + _results = IR.Type[value,] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(index) && push!(_attributes, namedattribute("index", index)) + + return IR.create_operation( + "pdl_interp.get_operands", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_result` + +`pdl_interp.get_result` operations try to get a specific result from an +operation. If the operation does not have a result for the given index, a +null value is returned. + +# Example + +```mlir +%result = pdl_interp.get_result 1 of %op +``` +""" +function get_result(inputOp::Value; value::IR.Type, index, location=Location()) + _results = IR.Type[value,] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("index", index),] + + return IR.create_operation( + "pdl_interp.get_result", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_results` + +`pdl_interp.get_results` operations try to get a specific result group +from an operation. If the expected result is a single Value, null is +returned if the result group is not of size 1. If a range is expected, +null is returned if the result group is invalid. If no index is provided, +the returned operand group corresponds to all results of the operation. + +# Example + +```mlir +// Get the first group of results from an operation, and expect a single +// element. +%result = pdl_interp.get_results 0 of %op : !pdl.value + +// Get the first group of results from an operation. +%results = pdl_interp.get_results 0 of %op : !pdl.range + +// Get all of the results from an operation. +%results = pdl_interp.get_results of %op : !pdl.range +``` +""" +function get_results(inputOp::Value; value::IR.Type, index=nothing, location=Location()) + _results = IR.Type[value,] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(index) && push!(_attributes, namedattribute("index", index)) + + return IR.create_operation( + "pdl_interp.get_results", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_users` + +`pdl_interp.get_users` extracts the users that accept this value. In the +case of a range, the union of users of the all the values are returned, +similarly to ResultRange::getUsers. + +# Example + +```mlir +// Get all the users of a single value. +%ops = pdl_interp.get_users of %value : !pdl.value + +// Get all the users of the first value in a range. +%ops = pdl_interp.get_users of %values : !pdl.range +``` +""" +function get_users(value::Value; operations::IR.Type, location=Location()) + _results = IR.Type[operations,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.get_users", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_value_type` + +`pdl_interp.get_value_type` operations get the resulting type of a specific +value or range thereof. + +# Example + +```mlir +// Get the type of a single value. +%type = pdl_interp.get_value_type of %value : !pdl.type + +// Get the types of a value range. +%type = pdl_interp.get_value_type of %values : !pdl.range +``` +""" +function get_value_type(value::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.get_value_type", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`is_not_null` + +`pdl_interp.is_not_null` operations check that a positional value or range +exists. For ranges, this does not mean that the range was simply empty. On +success, this operation branches to the true destination. Otherwise, the +false destination is taken. + +# Example + +```mlir +pdl_interp.is_not_null %value : !pdl.value -> ^matchDest, ^failureDest +``` +""" +function is_not_null(value::Value; trueDest::Block, falseDest::Block, location=Location()) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[trueDest, falseDest] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.is_not_null", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`record_match` + +`pdl_interp.record_match` operations record a successful pattern match with +the interpreter and branch to the next part of the matcher. The metadata +recorded by these operations correspond to a specific `pdl.pattern`, as well +as what values were used during that match that should be propagated to the +rewriter. + +# Example + +```mlir +pdl_interp.record_match @rewriters::myRewriter(%root : !pdl.operation) : benefit(1), loc([%root, %op1]), root(\"foo.op\") -> ^nextDest +``` +""" +function record_match( + inputs::Vector{Value}, + matchedOps::Vector{Value}; + rewriter, + rootKind=nothing, + generatedOps=nothing, + benefit, + dest::Block, + location=Location(), +) + _results = IR.Type[] + _operands = Value[inputs..., matchedOps...] + _owned_regions = Region[] + _successors = Block[dest,] + _attributes = NamedAttribute[ + namedattribute("rewriter", rewriter), namedattribute("benefit", benefit) + ] + push!(_attributes, operandsegmentsizes([length(inputs), length(matchedOps)])) + !isnothing(rootKind) && push!(_attributes, namedattribute("rootKind", rootKind)) + !isnothing(generatedOps) && + push!(_attributes, namedattribute("generatedOps", generatedOps)) + + return IR.create_operation( + "pdl_interp.record_match", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`replace` + +`pdl_interp.replaced` operations are used to specify that an operation +should be marked as replaced. The semantics of this operation correspond +with the `replaceOp` method on a `PatternRewriter`. The set of replacement +values must match the number of results specified by the operation. + +# Example + +```mlir +// Replace root node with 2 values: +pdl_interp.replace %root with (%val0, %val1 : !pdl.type, !pdl.type) +``` +""" +function replace(inputOp::Value, replValues::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[inputOp, replValues...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "pdl_interp.replace", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`switch_attribute` + +`pdl_interp.switch_attribute` operations compare the value of a given +attribute with a set of constant attributes. If the value matches one of the +provided case values the destination for that case value is taken, otherwise +the default destination is taken. + +# Example + +```mlir +pdl_interp.switch_attribute %attr to [10, true](^10Dest, ^trueDest) -> ^defaultDest +``` +""" +function switch_attribute( + attribute::Value; + caseValues, + defaultDest::Block, + cases::Vector{Block}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[attribute,] + _owned_regions = Region[] + _successors = Block[defaultDest, cases...] + _attributes = NamedAttribute[namedattribute("caseValues", caseValues),] + + return IR.create_operation( + "pdl_interp.switch_attribute", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`switch_operand_count` + +`pdl_interp.switch_operand_count` operations compare the operand count of a +given operation with a set of potential counts. If the value matches one of +the provided case values the destination for that case value is taken, +otherwise the default destination is taken. + +# Example + +```mlir +pdl_interp.switch_operand_count of %op to [10, 2] -> ^10Dest, ^2Dest, ^defaultDest +``` +""" +function switch_operand_count( + inputOp::Value; + caseValues, + defaultDest::Block, + cases::Vector{Block}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[defaultDest, cases...] + _attributes = NamedAttribute[namedattribute("caseValues", caseValues),] + + return IR.create_operation( + "pdl_interp.switch_operand_count", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`switch_operation_name` + +`pdl_interp.switch_operation_name` operations compare the name of a given +operation with a set of known names. If the value matches one of the +provided case values the destination for that case value is taken, otherwise +the default destination is taken. + +# Example + +```mlir +pdl_interp.switch_operation_name of %op to [\"foo.op\", \"bar.op\"](^fooDest, ^barDest) -> ^defaultDest +``` +""" +function switch_operation_name( + inputOp::Value; + caseValues, + defaultDest::Block, + cases::Vector{Block}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[defaultDest, cases...] + _attributes = NamedAttribute[namedattribute("caseValues", caseValues),] + + return IR.create_operation( + "pdl_interp.switch_operation_name", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`switch_result_count` + +`pdl_interp.switch_result_count` operations compare the result count of a +given operation with a set of potential counts. If the value matches one of +the provided case values the destination for that case value is taken, +otherwise the default destination is taken. + +# Example + +```mlir +pdl_interp.switch_result_count of %op to [0, 2](^0Dest, ^2Dest) -> ^defaultDest +``` +""" +function switch_result_count( + inputOp::Value; + caseValues, + defaultDest::Block, + cases::Vector{Block}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[inputOp,] + _owned_regions = Region[] + _successors = Block[defaultDest, cases...] + _attributes = NamedAttribute[namedattribute("caseValues", caseValues),] + + return IR.create_operation( + "pdl_interp.switch_result_count", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`switch_type` + +`pdl_interp.switch_type` operations compare a type with a set of statically +known types. If the value matches one of the provided case values the +destination for that case value is taken, otherwise the default destination +is taken. + +# Example + +```mlir +pdl_interp.switch_type %type to [i32, i64] -> ^i32Dest, ^i64Dest, ^defaultDest +``` +""" +function switch_type( + value::Value; caseValues, defaultDest::Block, cases::Vector{Block}, location=Location() +) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[defaultDest, cases...] + _attributes = NamedAttribute[namedattribute("caseValues", caseValues),] + + return IR.create_operation( + "pdl_interp.switch_type", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`switch_types` + +`pdl_interp.switch_types` operations compare a range of types with a set of +statically known ranges. If the value matches one of the provided case +values the destination for that case value is taken, otherwise the default +destination is taken. + +# Example + +```mlir +pdl_interp.switch_types %type is [[i32], [i64, i64]] -> ^i32Dest, ^i64Dest, ^defaultDest +``` +""" +function switch_types( + value::Value; caseValues, defaultDest::Block, cases::Vector{Block}, location=Location() +) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[defaultDest, cases...] + _attributes = NamedAttribute[namedattribute("caseValues", caseValues),] + + return IR.create_operation( + "pdl_interp.switch_types", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # pdl_interp diff --git a/src/Dialects/19/Polynomial.jl b/src/Dialects/19/Polynomial.jl new file mode 100644 index 00000000..3a4d33f3 --- /dev/null +++ b/src/Dialects/19/Polynomial.jl @@ -0,0 +1,490 @@ +module polynomial + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`add` + +Performs polynomial addition on the operands. The operands may be single +polynomials or containers of identically-typed polynomials, i.e., polynomials +from the same underlying ring with the same coefficient types. + +Addition is defined to occur in the ring defined by the ring attribute of +the two operands, meaning the addition is taken modulo the coefficientModulus +and the polynomialModulus of the ring. + +# Example + +```mlir +// add two polynomials modulo x^1024 - 1 +#poly = #polynomial.int_polynomial +#ring = #polynomial.ring +%0 = polynomial.constant int<1 + x**2> : !polynomial.polynomial<#ring> +%1 = polynomial.constant int : !polynomial.polynomial<#ring> +%2 = polynomial.add %0, %1 : !polynomial.polynomial<#ring> +``` +""" +function add( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "polynomial.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`constant` + +# Example + +```mlir +!int_poly_ty = !polynomial.polynomial> +%0 = polynomial.constant int<1 + x**2> : !int_poly_ty + +!float_poly_ty = !polynomial.polynomial> +%1 = polynomial.constant float<0.5 + 1.3e06 x**2> : !float_poly_ty +``` +""" +function constant(; output=nothing::Union{Nothing,IR.Type}, value, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "polynomial.constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`from_tensor` + +`polynomial.from_tensor` creates a polynomial value from a tensor of coefficients. +The input tensor must list the coefficients in degree-increasing order. + +The input one-dimensional tensor may have size at most the degree of the +ring\'s polynomialModulus generator polynomial, with smaller dimension implying that +all higher-degree terms have coefficient zero. + +# Example + +```mlir +#poly = #polynomial.int_polynomial +#ring = #polynomial.ring +%two = arith.constant 2 : i32 +%five = arith.constant 5 : i32 +%coeffs = tensor.from_elements %two, %two, %five : tensor<3xi32> +%poly = polynomial.from_tensor %coeffs : tensor<3xi32> -> !polynomial.polynomial<#ring> +``` +""" +function from_tensor(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "polynomial.from_tensor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`intt` + +`polynomial.intt` computes the reverse integer Number Theoretic Transform +(INTT) on the input tensor. This is the inverse operation of the +`polynomial.ntt` operation. + +The input tensor is interpreted as a point-value representation of the +output polynomial at powers of a primitive `n`-th root of unity (see +`polynomial.ntt`). The ring of the polynomial is taken from the required +encoding attribute of the tensor. + +The choice of primitive root may be optionally specified. +""" +function intt(input::Value; output::IR.Type, root=nothing, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(root) && push!(_attributes, namedattribute("root", root)) + + return IR.create_operation( + "polynomial.intt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`leading_term` + +The degree of a polynomial is the largest \$k\$ for which the coefficient +`a_k` of `x^k` is nonzero. The leading term is the term `a_k * x^k`, which +this op represents as a pair of results. The first is the degree `k` as an +index, and the second is the coefficient, whose type matches the +coefficient type of the polynomial\'s ring attribute. + +# Example + +```mlir +#poly = #polynomial.int_polynomial +#ring = #polynomial.ring +%0 = polynomial.constant int<1 + x**2> : !polynomial.polynomial<#ring> +%1, %2 = polynomial.leading_term %0 : !polynomial.polynomial<#ring> -> (index, i32) +``` +""" +function leading_term( + input::Value; degree::IR.Type, coefficient::IR.Type, location=Location() +) + _results = IR.Type[degree, coefficient] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "polynomial.leading_term", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`monic_monomial_mul` + +Multiply a polynomial by a monic monomial, meaning a polynomial of the form +`1 * x^k` for an index operand `k`. + +In some special rings of polynomials, such as a ring of polynomials +modulo `x^n - 1`, `monomial_mul` can be interpreted as a cyclic shift of +the coefficients of the polynomial. For some rings, this results in +optimized lowerings that involve rotations and rescaling of the +coefficients of the input. +""" +function monic_monomial_mul( + input::Value, + monomialDegree::Value; + output=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[input, monomialDegree] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "polynomial.monic_monomial_mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`monomial` + +Construct a polynomial that consists of a single monomial term, from its +degree and coefficient as dynamic inputs. + +The coefficient type of the output polynomial\'s ring attribute must match +the `coefficient` input type. + +# Example + +```mlir +#poly = #polynomial.int_polynomial +#ring = #polynomial.ring +%deg = arith.constant 1023 : index +%five = arith.constant 5 : i32 +%0 = polynomial.monomial %five, %deg : (i32, index) -> !polynomial.polynomial<#ring> +``` +""" +function monomial(coefficient::Value, degree::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[coefficient, degree] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "polynomial.monomial", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mul` + +Performs polynomial multiplication on the operands. The operands may be single +polynomials or containers of identically-typed polynomials, i.e., polynomials +from the same underlying ring with the same coefficient types. + +Multiplication is defined to occur in the ring defined by the ring attribute of +the two operands, meaning the multiplication is taken modulo the coefficientModulus +and the polynomialModulus of the ring. + +# Example + +```mlir +// multiply two polynomials modulo x^1024 - 1 +#poly = #polynomial.int_polynomial +#ring = #polynomial.ring +%0 = polynomial.constant int<1 + x**2> : !polynomial.polynomial<#ring> +%1 = polynomial.constant int : !polynomial.polynomial<#ring> +%2 = polynomial.mul %0, %1 : !polynomial.polynomial<#ring> +``` +""" +function mul( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "polynomial.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mul_scalar` + +Multiplies the polynomial operand\'s coefficients by a given scalar value. +The operation is defined to occur in the ring defined by the ring attribute +of the two operands, meaning the multiplication is taken modulo the +coefficientModulus of the ring. + +The `scalar` input must have the same type as the polynomial ring\'s +coefficientType. + +# Example + +```mlir +// multiply two polynomials modulo x^1024 - 1 +#poly = #polynomial.int_polynomial +#ring = #polynomial.ring +%0 = polynomial.constant int<1 + x**2> : !polynomial.polynomial<#ring> +%1 = arith.constant 3 : i32 +%2 = polynomial.mul_scalar %0, %1 : !polynomial.polynomial<#ring>, i32 +``` +""" +function mul_scalar( + polynomial::Value, + scalar::Value; + output=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[polynomial, scalar] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "polynomial.mul_scalar", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ntt` + +`polynomial.ntt` computes the forward integer Number Theoretic Transform +(NTT) on the input polynomial. It returns a tensor containing a point-value +representation of the input polynomial. The output tensor has shape equal +to the degree of the ring\'s `polynomialModulus`. The polynomial\'s RingAttr +is embedded as the encoding attribute of the output tensor. + +Given an input polynomial `F(x)` over a ring whose `polynomialModulus` has +degree `n`, and a primitive `n`-th root of unity `omega_n`, the output is +the list of \$n\$ evaluations + + `f[k] = F(omega[n]^k) ; k = {0, ..., n-1}` + +The choice of primitive root may be optionally specified. +""" +function ntt(input::Value; output::IR.Type, root=nothing, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(root) && push!(_attributes, namedattribute("root", root)) + + return IR.create_operation( + "polynomial.ntt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sub` + +Performs polynomial subtraction on the operands. The operands may be single +polynomials or containers of identically-typed polynomials, i.e., polynomials +from the same underlying ring with the same coefficient types. + +Subtraction is defined to occur in the ring defined by the ring attribute of +the two operands, meaning the subtraction is taken modulo the coefficientModulus +and the polynomialModulus of the ring. + +# Example + +```mlir +// subtract two polynomials modulo x^1024 - 1 +#poly = #polynomial.int_polynomial +#ring = #polynomial.ring +%0 = polynomial.constant int<1 + x**2> : !polynomial.polynomial<#ring> +%1 = polynomial.constant int : !polynomial.polynomial<#ring> +%2 = polynomial.sub %0, %1 : !polynomial.polynomial<#ring> +``` +""" +function sub( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "polynomial.sub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`to_tensor` + +`polynomial.to_tensor` creates a dense tensor value containing the +coefficients of the input polynomial. The output tensor contains the +coefficients in degree-increasing order. + +Operations that act on the coefficients of a polynomial, such as extracting +a specific coefficient or extracting a range of coefficients, should be +implemented by composing `to_tensor` with the relevant `tensor` dialect +ops. + +The output tensor has shape equal to the degree of the polynomial ring +attribute\'s polynomialModulus, including zeroes. + +# Example + +```mlir +#poly = #polynomial.int_polynomial +#ring = #polynomial.ring +%two = arith.constant 2 : i32 +%five = arith.constant 5 : i32 +%coeffs = tensor.from_elements %two, %two, %five : tensor<3xi32> +%poly = polynomial.from_tensor %coeffs : tensor<3xi32> -> !polynomial.polynomial<#ring> +%tensor = polynomial.to_tensor %poly : !polynomial.polynomial<#ring> -> tensor<1024xi32> +``` +""" +function to_tensor(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "polynomial.to_tensor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # polynomial diff --git a/src/Dialects/19/Quant.jl b/src/Dialects/19/Quant.jl new file mode 100644 index 00000000..623de142 --- /dev/null +++ b/src/Dialects/19/Quant.jl @@ -0,0 +1,123 @@ +module quant + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`dcast` + +A DequantizeCast op `dcast` represents the inverse of a `qcast`, +converting back from a quantized to quantizable (expressed) type. + +Like `qcast`s, a `dcast` is allowed to have both its operand and result +as non quantized types. This facilitates transformations and marks edges +where the computation must be carried out in the expressed type. + +Especially early in transformation, it is common to have `dcast`s on +all operands to ops that must operate with the expressed type (typically +math ops prior to lowering to target-specific, quantized kernels). +""" +function dcast(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "quant.dcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`qcast` + +A QuantizeCast `qcast` represents a potential type shift from a quantizable +type to a quantized type. + +At runtime, a `qcast` will apply the transformation expressed by its +operand and result type. For flexibility during transformation, it is also +possible to have a `qcast` that performs no transformation (both its +operand and result type are quantizable). + +A `qcast` will typically originate from either: + a) An expressed or implied constraint in the source dialect which signals + that a certain level of quantization is possible or required. + b) An inference made by a quantization algorithm indicating that a + quantized representation may be acceptable. + +Especially early in transformation, it is common to have pairs of +`qcast` and `dcast` at points where a transition to a quantized type is +required. In addition, it is also common to have an identity `qcast` +(where the operand and result type are not quantized) at all points where +it is legal to use a quantized representation (but is not known to be +acceptable). +""" +function qcast(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "quant.qcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`scast` + +A StorageCast `scast` represents a cast from or to a type based on the +storage type and a type based on a corresponding quantized type. + +This op exists to ensure type coherency for between parts of the computation +which are operating directly on an underlying storage type and those which +operate on quantized values. + +Examples from storage to quantized type: +``` +i8 -> !quant<\"uniform[i8:f32]{1.0}\"> +``` +``` +tensor<4xi8> -> tensor<4x!quant<\"uniform[i8:f32]{1.0}\">> +``` +``` +vector<4xi8> -> vector<4x!quant<\"uniform[i8:f32]{1.0}\">> +``` +""" +function scast(arg::Value; res::IR.Type, location=Location()) + _results = IR.Type[res,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "quant.scast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # quant diff --git a/src/Dialects/19/SCF.jl b/src/Dialects/19/SCF.jl new file mode 100644 index 00000000..175a775c --- /dev/null +++ b/src/Dialects/19/SCF.jl @@ -0,0 +1,935 @@ +module scf + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`condition` + +This operation accepts the continuation (i.e., inverse of exit) condition +of the `scf.while` construct. If its first argument is true, the \"after\" +region of `scf.while` is executed, with the remaining arguments forwarded +to the entry block of the region. Otherwise, the loop terminates. +""" +function condition(condition::Value, args::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[condition, args...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "scf.condition", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`execute_region` + +The `execute_region` operation is used to allow multiple blocks within SCF +and other operations which can hold only one block. The `execute_region` +operation executes the region held exactly once and cannot have any operands. +As such, its region has no arguments. All SSA values that dominate the op can +be accessed inside the op. The op\'s region can have multiple blocks and the +blocks can have multiple distinct terminators. Values returned from this op\'s +region define the op\'s results. + +# Example + +```mlir +scf.for %i = 0 to 128 step %c1 { + %y = scf.execute_region -> i32 { + %x = load %A[%i] : memref<128xi32> + scf.yield %x : i32 + } +} + +affine.for %i = 0 to 100 { + \"foo\"() : () -> () + %v = scf.execute_region -> i64 { + cf.cond_br %cond, ^bb1, ^bb2 + + ^bb1: + %c1 = arith.constant 1 : i64 + cf.br ^bb3(%c1 : i64) + + ^bb2: + %c2 = arith.constant 2 : i64 + cf.br ^bb3(%c2 : i64) + + ^bb3(%x : i64): + scf.yield %x : i64 + } + \"bar\"(%v) : (i64) -> () +} +``` +""" +function execute_region(; result_0::Vector{IR.Type}, region::Region, location=Location()) + _results = IR.Type[result_0...,] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "scf.execute_region", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`for_` + +The `scf.for` operation represents a loop taking 3 SSA value as operands +that represent the lower bound, upper bound and step respectively. The +operation defines an SSA value for its induction variable. It has one +region capturing the loop body. The induction variable is represented as an +argument of this region. This SSA value is a signless integer or index. +The step is a value of same type but required to be positive. The lower and +upper bounds specify a half-open range: the range includes the lower bound +but does not include the upper bound. + +The body region must contain exactly one block that terminates with +`scf.yield`. Calling ForOp::build will create such a region and insert +the terminator implicitly if none is defined, so will the parsing even in +cases when it is absent from the custom format. For example: + +```mlir +// Index case. +scf.for %iv = %lb to %ub step %step { + ... // body +} +... +// Integer case. +scf.for %iv_32 = %lb_32 to %ub_32 step %step_32 : i32 { + ... // body +} +``` + +`scf.for` can also operate on loop-carried variables and returns the final +values after loop termination. The initial values of the variables are +passed as additional SSA operands to the `scf.for` following the 3 loop +control SSA values mentioned above (lower bound, upper bound and step). The +operation region has an argument for the induction variable, followed by +one argument for each loop-carried variable, representing the value of the +variable at the current iteration. + +The region must terminate with a `scf.yield` that passes the current +values of all loop-carried variables to the next iteration, or to the +`scf.for` result, if at the last iteration. The static type of a +loop-carried variable may not change with iterations; its runtime type is +allowed to change. Note, that when the loop-carried variables are present, +calling ForOp::build will not insert the terminator implicitly. The caller +must insert `scf.yield` in that case. + +`scf.for` results hold the final values after the last iteration. +For example, to sum-reduce a memref: + +```mlir +func.func @reduce(%buffer: memref<1024xf32>, %lb: index, + %ub: index, %step: index) -> (f32) { + // Initial sum set to 0. + %sum_0 = arith.constant 0.0 : f32 + // iter_args binds initial values to the loop\'s region arguments. + %sum = scf.for %iv = %lb to %ub step %step + iter_args(%sum_iter = %sum_0) -> (f32) { + %t = load %buffer[%iv] : memref<1024xf32> + %sum_next = arith.addf %sum_iter, %t : f32 + // Yield current iteration sum to next iteration %sum_iter or to %sum + // if final iteration. + scf.yield %sum_next : f32 + } + return %sum : f32 +} +``` + +If the `scf.for` defines any values, a yield must be explicitly present. +The number and types of the `scf.for` results must match the initial +values in the `iter_args` binding and the yield operands. + +Another example with a nested `scf.if` (see `scf.if` for details) to +perform conditional reduction: + +```mlir +func.func @conditional_reduce(%buffer: memref<1024xf32>, %lb: index, + %ub: index, %step: index) -> (f32) { + %sum_0 = arith.constant 0.0 : f32 + %c0 = arith.constant 0.0 : f32 + %sum = scf.for %iv = %lb to %ub step %step + iter_args(%sum_iter = %sum_0) -> (f32) { + %t = load %buffer[%iv] : memref<1024xf32> + %cond = arith.cmpf \"ugt\", %t, %c0 : f32 + %sum_next = scf.if %cond -> (f32) { + %new_sum = arith.addf %sum_iter, %t : f32 + scf.yield %new_sum : f32 + } else { + scf.yield %sum_iter : f32 + } + scf.yield %sum_next : f32 + } + return %sum : f32 +} +``` +""" +function for_( + lowerBound::Value, + upperBound::Value, + step::Value, + initArgs::Vector{Value}; + results::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[lowerBound, upperBound, step, initArgs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "scf.for", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`forall` + +`scf.forall` is a target-independent multi-dimensional parallel +region application operation. It has exactly one block that represents the +parallel body and it takes index operands that specify lower bounds, upper +bounds and steps. + +The op also takes a variadic number of tensor operands (`shared_outs`). +The future buffers corresponding to these tensors are shared among all +threads. Shared tensors should be accessed via their corresponding block +arguments. If multiple threads write to a shared buffer in a racy +fashion, these writes will execute in some unspecified order. Tensors that +are not shared can be used inside the body (i.e., the op is not isolated +from above); however, if a use of such a tensor bufferizes to a memory +write, the tensor is privatized, i.e., a thread-local copy of the tensor is +used. This ensures that memory side effects of a thread are not visible to +other threads (or in the parent body), apart from explicitly shared tensors. + +The name \"thread\" conveys the fact that the parallel execution is mapped +(i.e. distributed) to a set of virtual threads of execution, one function +application per thread. Further lowerings are responsible for specifying +how this is materialized on concrete hardware resources. + +An optional `mapping` is an attribute array that specifies processing units +with their dimension, how it remaps 1-1 to a set of concrete processing +element resources (e.g. a CUDA grid dimension or a level of concrete nested +async parallelism). It is expressed via any attribute that implements the +device mapping interface. It is the reponsibility of the lowering mechanism +to interpret the `mapping` attributes in the context of the concrete target +the op is lowered to, or to ignore it when the specification is ill-formed +or unsupported for a particular target. + +The only allowed terminator is `scf.forall.in_parallel`. +`scf.forall` returns one value per `shared_out` operand. The +actions of the `in_parallel` terminators specify how to combine the +partial results of all parallel invocations into a full value, in some +unspecified order. The \"destination\" of each such op must be a `shared_out` +block argument of the `scf.forall` op. + +The actions involved in constructing the return values are further described +by `tensor.parallel_insert_slice`. + +`scf.forall` acts as an implicit synchronization point. + +When the parallel function body has side effects, their order is unspecified +across threads. + +`scf.forall` can be printed in two different ways depending on +whether the loop is normalized or not. The loop is \'normalized\' when all +lower bounds are equal to zero and steps are equal to one. In that case, +`lowerBound` and `step` operands will be omitted during printing. + +Normalized loop example: + +```mlir +// +// Sequential context. +// +%matmul_and_pointwise:2 = scf.forall (%thread_id_1, %thread_id_2) in + (%num_threads_1, %numthread_id_2) shared_outs(%o1 = %C, %o2 = %pointwise) + -> (tensor, tensor) { + // + // Parallel context, each thread with id = (%thread_id_1, %thread_id_2) + // runs its version of the code. + // + %sA = tensor.extract_slice %A[f((%thread_id_1, %thread_id_2))]: + tensor to tensor + %sB = tensor.extract_slice %B[g((%thread_id_1, %thread_id_2))]: + tensor to tensor + %sC = tensor.extract_slice %o1[h((%thread_id_1, %thread_id_2))]: + tensor to tensor + %sD = linalg.matmul + ins(%sA, %sB : tensor, tensor) + outs(%sC : tensor) + + %spointwise = subtensor %o2[i((%thread_id_1, %thread_id_2))]: + tensor to tensor + %sE = linalg.add ins(%spointwise : tensor) outs(%sD : tensor) + + scf.forall.in_parallel { + tensor.parallel_insert_slice %sD into %o1[h((%thread_id_1, %thread_id_2))]: + tensor into tensor + + tensor.parallel_insert_slice %spointwise into %o2[i((%thread_id_1, %thread_id_2))]: + tensor into tensor + } +} +// Implicit synchronization point. +// Sequential context. +// +``` + +Loop with loop bounds example: + +```mlir +// +// Sequential context. +// +%pointwise = scf.forall (%i, %j) = (0, 0) to (%dim1, %dim2) + step (%tileSize1, %tileSize2) shared_outs(%o1 = %out) + -> (tensor, tensor) { + // + // Parallel context. + // + %sA = tensor.extract_slice %A[%i, %j][%tileSize1, %tileSize2][1, 1] + : tensor to tensor + %sB = tensor.extract_slice %B[%i, %j][%tileSize1, %tileSize2][1, 1] + : tensor to tensor + %sC = tensor.extract_slice %o[%i, %j][%tileSize1, %tileSize2][1, 1] + : tensor to tensor + + %add = linalg.map {\"arith.addf\"} + ins(%sA, %sB : tensor, tensor) + outs(%sC : tensor) + + scf.forall.in_parallel { + tensor.parallel_insert_slice %add into + %o[%i, %j][%tileSize1, %tileSize2][1, 1] + : tensor into tensor + } +} +// Implicit synchronization point. +// Sequential context. +// +``` + +Example with mapping attribute: + +```mlir +// +// Sequential context. Here `mapping` is expressed as GPU thread mapping +// attributes +// +%matmul_and_pointwise:2 = scf.forall (%thread_id_1, %thread_id_2) in + (%num_threads_1, %numthread_id_2) shared_outs(...) + -> (tensor, tensor) { + // + // Parallel context, each thread with id = **(%thread_id_2, %thread_id_1)** + // runs its version of the code. + // + scf.forall.in_parallel { + ... + } +} { mapping = [#gpu.thread, #gpu.thread] } +// Implicit synchronization point. +// Sequential context. +// +``` + +Example with privatized tensors: + +```mlir +%t0 = ... +%t1 = ... +%r = scf.forall ... shared_outs(%o = t0) -> tensor { + // %t0 and %t1 are privatized. %t0 is definitely copied for each thread + // because the scf.forall op\'s %t0 use bufferizes to a memory + // write. In the absence of other conflicts, %t1 is copied only if there + // are uses of %t1 in the body that bufferize to a memory read and to a + // memory write. + \"some_use\"(%t0) + \"some_use\"(%t1) +} +``` +""" +function forall( + dynamicLowerBound::Vector{Value}, + dynamicUpperBound::Vector{Value}, + dynamicStep::Vector{Value}, + outputs::Vector{Value}; + results::Vector{IR.Type}, + staticLowerBound, + staticUpperBound, + staticStep, + mapping=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[ + dynamicLowerBound..., dynamicUpperBound..., dynamicStep..., outputs... + ] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("staticLowerBound", staticLowerBound), + namedattribute("staticUpperBound", staticUpperBound), + namedattribute("staticStep", staticStep), + ] + push!( + _attributes, + operandsegmentsizes([ + length(dynamicLowerBound), + length(dynamicUpperBound), + length(dynamicStep), + length(outputs), + ]), + ) + !isnothing(mapping) && push!(_attributes, namedattribute("mapping", mapping)) + + return IR.create_operation( + "scf.forall", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`if_` + +The `scf.if` operation represents an if-then-else construct for +conditionally executing two regions of code. The operand to an if operation +is a boolean value. For example: + +```mlir +scf.if %b { + ... +} else { + ... +} +``` + +`scf.if` may also produce results. Which values are returned depends on +which execution path is taken. + +# Example + +```mlir +%x, %y = scf.if %b -> (f32, f32) { + %x_true = ... + %y_true = ... + scf.yield %x_true, %y_true : f32, f32 +} else { + %x_false = ... + %y_false = ... + scf.yield %x_false, %y_false : f32, f32 +} +``` + +The \"then\" region has exactly 1 block. The \"else\" region may have 0 or 1 +block. In case the `scf.if` produces results, the \"else\" region must also +have exactly 1 block. + +The blocks are always terminated with `scf.yield`. If `scf.if` defines no +values, the `scf.yield` can be left out, and will be inserted implicitly. +Otherwise, it must be explicit. + +# Example + +```mlir +scf.if %b { + ... +} +``` + +The types of the yielded values must match the result types of the +`scf.if`. +""" +function if_( + condition::Value; + results::Vector{IR.Type}, + thenRegion::Region, + elseRegion::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[condition,] + _owned_regions = Region[thenRegion, elseRegion] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "scf.if", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`forall_in_parallel` + +`scf.forall.in_parallel` is a designated terminator for +the `scf.forall` operation. + +It has a single region with a single block that contains a flat list of ops. +Each such op participates in the aggregate formation of a single result of +the enclosing `scf.forall`. +The result number corresponds to the position of the op in the terminator. +""" +function forall_in_parallel(; region::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "scf.forall.in_parallel", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`index_switch` + +The `scf.index_switch` is a control-flow operation that branches to one of +the given regions based on the values of the argument and the cases. The +argument is always of type `index`. + +The operation always has a \"default\" region and any number of case regions +denoted by integer constants. Control-flow transfers to the case region +whose constant value equals the value of the argument. If the argument does +not equal any of the case values, control-flow transfer to the \"default\" +region. + +# Example + +```mlir +%0 = scf.index_switch %arg0 : index -> i32 +case 2 { + %1 = arith.constant 10 : i32 + scf.yield %1 : i32 +} +case 5 { + %2 = arith.constant 20 : i32 + scf.yield %2 : i32 +} +default { + %3 = arith.constant 30 : i32 + scf.yield %3 : i32 +} +``` +""" +function index_switch( + arg::Value; + results::Vector{IR.Type}, + cases, + defaultRegion::Region, + caseRegions::Vector{Region}, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[arg,] + _owned_regions = Region[defaultRegion, caseRegions...] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("cases", cases),] + + return IR.create_operation( + "scf.index_switch", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`parallel` + +The \"scf.parallel\" operation represents a loop nest taking 4 groups of SSA +values as operands that represent the lower bounds, upper bounds, steps and +initial values, respectively. The operation defines a variadic number of +SSA values for its induction variables. It has one region capturing the +loop body. The induction variables are represented as an argument of this +region. These SSA values always have type index, which is the size of the +machine word. The steps are values of type index, required to be positive. +The lower and upper bounds specify a half-open range: the range includes +the lower bound but does not include the upper bound. The initial values +have the same types as results of \"scf.parallel\". If there are no results, +the keyword `init` can be omitted. + +Semantically we require that the iteration space can be iterated in any +order, and the loop body can be executed in parallel. If there are data +races, the behavior is undefined. + +The parallel loop operation supports reduction of values produced by +individual iterations into a single result. This is modeled using the +\"scf.reduce\" terminator operation (see \"scf.reduce\" for details). The i-th +result of an \"scf.parallel\" operation is associated with the i-th initial +value operand, the i-th operand of the \"scf.reduce\" operation (the value to +be reduced) and the i-th region of the \"scf.reduce\" operation (the reduction +function). Consequently, we require that the number of results of an +\"scf.parallel\" op matches the number of initial values and the the number of +reductions in the \"scf.reduce\" terminator. + +The body region must contain exactly one block that terminates with a +\"scf.reduce\" operation. If an \"scf.parallel\" op has no reductions, the +terminator has no operands and no regions. The \"scf.parallel\" parser will +automatically insert the terminator for ops that have no reductions if it is +absent. + +# Example + +```mlir +%init = arith.constant 0.0 : f32 +%r:2 = scf.parallel (%iv) = (%lb) to (%ub) step (%step) init (%init, %init) + -> f32, f32 { + %elem_to_reduce1 = load %buffer1[%iv] : memref<100xf32> + %elem_to_reduce2 = load %buffer2[%iv] : memref<100xf32> + scf.reduce(%elem_to_reduce1, %elem_to_reduce2 : f32, f32) { + ^bb0(%lhs : f32, %rhs: f32): + %res = arith.addf %lhs, %rhs : f32 + scf.reduce.return %res : f32 + }, { + ^bb0(%lhs : f32, %rhs: f32): + %res = arith.mulf %lhs, %rhs : f32 + scf.reduce.return %res : f32 + } +} +``` +""" +function parallel( + lowerBound::Vector{Value}, + upperBound::Vector{Value}, + step::Vector{Value}, + initVals::Vector{Value}; + results::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[lowerBound..., upperBound..., step..., initVals...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + push!( + _attributes, + operandsegmentsizes([ + length(lowerBound), length(upperBound), length(step), length(initVals) + ]), + ) + + return IR.create_operation( + "scf.parallel", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reduce` + +\"scf.reduce\" is the terminator for \"scf.parallel\" operations. It can model +an arbitrary number of reductions. It has one region per reduction. Each +region has one block with two arguments which have the same type as the +corresponding operand of \"scf.reduce\". The operands of the op are the values +that should be reduce; one value per reduction. + +The i-th reduction (i.e., the i-th region and the i-th operand) corresponds +the i-th initial value and the i-th result of the enclosing \"scf.parallel\" +op. + +The \"scf.reduce\" operation contains regions whose entry blocks expect two +arguments of the same type as the corresponding operand. As the iteration +order of the enclosing parallel loop and hence reduction order is +unspecified, the results of the reductions may be non-deterministic unless +the reductions are associative and commutative. + +The result of a reduction region (\"scf.reduce.return\" operand) must have the +same type as the corresponding \"scf.reduce\" operand and the corresponding +\"scf.parallel\" initial value. + +# Example + +```mlir +%operand = arith.constant 1.0 : f32 +scf.reduce(%operand : f32) { + ^bb0(%lhs : f32, %rhs: f32): + %res = arith.addf %lhs, %rhs : f32 + scf.reduce.return %res : f32 +} +``` +""" +function reduce(operands::Vector{Value}; reductions::Vector{Region}, location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[reductions...,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "scf.reduce", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reduce_return` + +\"scf.reduce.return\" is a special terminator operation for the block inside +\"scf.reduce\" regions. It terminates the region. It should have the same +operand type as the corresponding operand of the enclosing \"scf.reduce\" op. + +# Example + +```mlir +scf.reduce.return %res : f32 +``` +""" +function reduce_return(result::Value; location=Location()) + _results = IR.Type[] + _operands = Value[result,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "scf.reduce.return", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`while_` + +This operation represents a generic \"while\"/\"do-while\" loop that keeps +iterating as long as a condition is satisfied. There is no restriction on +the complexity of the condition. It consists of two regions (with single +block each): \"before\" region and \"after\" region. The names of regions +indicates whether they execute before or after the condition check. +Therefore, if the main loop payload is located in the \"before\" region, the +operation is a \"do-while\" loop. Otherwise, it is a \"while\" loop. + +The \"before\" region terminates with a special operation, `scf.condition`, +that accepts as its first operand an `i1` value indicating whether to +proceed to the \"after\" region (value is `true`) or not. The two regions +communicate by means of region arguments. Initially, the \"before\" region +accepts as arguments the operands of the `scf.while` operation and uses them +to evaluate the condition. It forwards the trailing, non-condition operands +of the `scf.condition` terminator either to the \"after\" region if the +control flow is transferred there or to results of the `scf.while` operation +otherwise. The \"after\" region takes as arguments the values produced by the +\"before\" region and uses `scf.yield` to supply new arguments for the +\"before\" region, into which it transfers the control flow unconditionally. + +A simple \"while\" loop can be represented as follows. + +```mlir +%res = scf.while (%arg1 = %init1) : (f32) -> f32 { + // \"Before\" region. + // In a \"while\" loop, this region computes the condition. + %condition = call @evaluate_condition(%arg1) : (f32) -> i1 + + // Forward the argument (as result or \"after\" region argument). + scf.condition(%condition) %arg1 : f32 + +} do { +^bb0(%arg2: f32): + // \"After\" region. + // In a \"while\" loop, this region is the loop body. + %next = call @payload(%arg2) : (f32) -> f32 + + // Forward the new value to the \"before\" region. + // The operand types must match the types of the `scf.while` operands. + scf.yield %next : f32 +} +``` + +A simple \"do-while\" loop can be represented by reducing the \"after\" block +to a simple forwarder. + +```mlir +%res = scf.while (%arg1 = %init1) : (f32) -> f32 { + // \"Before\" region. + // In a \"do-while\" loop, this region contains the loop body. + %next = call @payload(%arg1) : (f32) -> f32 + + // And also evaluates the condition. + %condition = call @evaluate_condition(%arg1) : (f32) -> i1 + + // Loop through the \"after\" region. + scf.condition(%condition) %next : f32 + +} do { +^bb0(%arg2: f32): + // \"After\" region. + // Forwards the values back to \"before\" region unmodified. + scf.yield %arg2 : f32 +} +``` + +Note that the types of region arguments need not to match with each other. +The op expects the operand types to match with argument types of the +\"before\" region; the result types to match with the trailing operand types +of the terminator of the \"before\" region, and with the argument types of the +\"after\" region. The following scheme can be used to share the results of +some operations executed in the \"before\" region with the \"after\" region, +avoiding the need to recompute them. + +```mlir +%res = scf.while (%arg1 = %init1) : (f32) -> i64 { + // One can perform some computations, e.g., necessary to evaluate the + // condition, in the \"before\" region and forward their results to the + // \"after\" region. + %shared = call @shared_compute(%arg1) : (f32) -> i64 + + // Evaluate the condition. + %condition = call @evaluate_condition(%arg1, %shared) : (f32, i64) -> i1 + + // Forward the result of the shared computation to the \"after\" region. + // The types must match the arguments of the \"after\" region as well as + // those of the `scf.while` results. + scf.condition(%condition) %shared : i64 + +} do { +^bb0(%arg2: i64) { + // Use the partial result to compute the rest of the payload in the + // \"after\" region. + %res = call @payload(%arg2) : (i64) -> f32 + + // Forward the new value to the \"before\" region. + // The operand types must match the types of the `scf.while` operands. + scf.yield %res : f32 +} +``` + +The custom syntax for this operation is as follows. + +``` +op ::= `scf.while` assignments `:` function-type region `do` region + `attributes` attribute-dict +initializer ::= /* empty */ | `(` assignment-list `)` +assignment-list ::= assignment | assignment `,` assignment-list +assignment ::= ssa-value `=` ssa-value +``` +""" +function while_( + inits::Vector{Value}; + results::Vector{IR.Type}, + before::Region, + after::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[inits...,] + _owned_regions = Region[before, after] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "scf.while", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +\"scf.yield\" yields an SSA value from the SCF dialect op region and +terminates the regions. The semantics of how the values are yielded is +defined by the parent operation. +If \"scf.yield\" has any operands, the operands must match the parent +operation\'s results. +If the parent operation defines no values, then the \"scf.yield\" may be +left out in the custom syntax and the builders will insert one implicitly. +Otherwise, it has to be present in the syntax to indicate which values are +yielded. +""" +function yield(results::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[results...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "scf.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # scf diff --git a/src/Dialects/19/SPIRV.jl b/src/Dialects/19/SPIRV.jl new file mode 100644 index 00000000..5be94471 --- /dev/null +++ b/src/Dialects/19/SPIRV.jl @@ -0,0 +1,12439 @@ +module spirv + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`AccessChain` + +Result Type must be an OpTypePointer. Its Type operand must be the type +reached by walking the Base’s type hierarchy down to the last provided +index in Indexes, and its Storage Class operand must be the same as the +Storage Class of Base. + +Base must be a pointer, pointing to the base of a composite object. + +Indexes walk the type hierarchy to the desired depth, potentially down +to scalar granularity. The first index in Indexes will select the top- +level member/element/component/element of the base composite. All +composite constituents use zero-based numbering, as described by their +OpType… instruction. The second index will apply similarly to that +result, and so on. Once any non-composite type is reached, there must be +no remaining (unused) indexes. + + Each index in Indexes + +- must be a scalar integer type, + +- is treated as a signed count, and + +- must be an OpConstant when indexing into a structure. + + +``` +access-chain-op ::= ssa-id `=` `spirv.AccessChain` ssa-use + `[` ssa-use (\',\' ssa-use)* `]` + `:` pointer-type +``` + +#### Example: + +```mlir +%0 = \"spirv.Constant\"() { value = 1: i32} : () -> i32 +%1 = spirv.Variable : !spirv.ptr>, Function> +%2 = spirv.AccessChain %1[%0] : !spirv.ptr>, Function> +%3 = spirv.Load \"Function\" %2 [\"Volatile\"] : !spirv.array<4xf32> +``` +""" +function AccessChain( + base_ptr::Value, indices::Vector{Value}; component_ptr::IR.Type, location=Location() +) + _results = IR.Type[component_ptr,] + _operands = Value[base_ptr, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.AccessChain", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mlir_addressof` + +Variables in module scope are defined using symbol names. This op generates +an SSA value that can be used to refer to the symbol within function scope +for use in ops that expect an SSA value. This operation has no corresponding +SPIR-V instruction; it\'s merely used for modelling purpose in the SPIR-V +dialect. Since variables in module scope in SPIR-V dialect are of pointer +type, this op returns a pointer type as well, and the type is the same as +the variable referenced. + +#### Example: + +```mlir +%0 = spirv.mlir.addressof @global_var : !spirv.ptr +``` +""" +function mlir_addressof(; pointer::IR.Type, variable, location=Location()) + _results = IR.Type[pointer,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("variable", variable),] + + return IR.create_operation( + "spirv.mlir.addressof", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`AtomicAnd` + +1) load through Pointer to get an Original Value, + +2) get a New Value by the bitwise AND of Original Value and Value, and + +3) store the New Value back through Pointer. + +The instruction’s result is the Original Value. + +Result Type must be an integer type scalar. + + The type of Value must be the same as Result Type. The type of the +value pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicAnd %pointer, %value : + !spirv.ptr +``` +""" +function AtomicAnd( + pointer::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicAnd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicCompareExchange` + +1) load through Pointer to get an Original Value, + +2) get a New Value from Value only if Original Value equals Comparator, +and + +3) store the New Value back through Pointer\'only if \'Original Value +equaled Comparator. + +The instruction\'s result is the Original Value. + +Result Type must be an integer type scalar. + +Use Equal for the memory semantics of this instruction when Value and +Original Value compare equal. + +Use Unequal for the memory semantics of this instruction when Value and +Original Value compare unequal. Unequal must not be set to Release or +Acquire and Release. In addition, Unequal cannot be set to a stronger +memory-order then Equal. + + The type of Value must be the same as Result Type. The type of the +value pointed to by Pointer must be the same as Result Type. This type +must also match the type of Comparator. + +Memory is a memory Scope. + + + +#### Example: + +``` +%0 = spirv.AtomicCompareExchange + %pointer, %value, %comparator + : !spirv.ptr +``` +""" +function AtomicCompareExchange( + pointer::Value, + value::Value, + comparator::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + equal_semantics, + unequal_semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value, comparator] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), + namedattribute("equal_semantics", equal_semantics), + namedattribute("unequal_semantics", unequal_semantics), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicCompareExchange", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicCompareExchangeWeak` + +Has the same semantics as OpAtomicCompareExchange. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicCompareExchangeWeak + %pointer, %value, %comparator + : !spirv.ptr +``` +""" +function AtomicCompareExchangeWeak( + pointer::Value, + value::Value, + comparator::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + equal_semantics, + unequal_semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value, comparator] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), + namedattribute("equal_semantics", equal_semantics), + namedattribute("unequal_semantics", unequal_semantics), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicCompareExchangeWeak", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicExchange` + +1) load through Pointer to get an Original Value, + +2) get a New Value from copying Value, and + +3) store the New Value back through Pointer. + +The instruction\'s result is the Original Value. + +Result Type must be a scalar of integer type or floating-point type. + + The type of Value must be the same as Result Type. The type of the +value pointed to by Pointer must be the same as Result Type. + +Memory is a memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicExchange %pointer, %value, + : !spirv.ptr +``` +""" +function AtomicExchange( + pointer::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicExchange", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicIAdd` + +1) load through Pointer to get an Original Value, + +2) get a New Value by integer addition of Original Value and Value, and + +3) store the New Value back through Pointer. + +The instruction’s result is the Original Value. + +Result Type must be an integer type scalar. + + The type of Value must be the same as Result Type. The type of the +value pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicIAdd %pointer, %value : + !spirv.ptr +``` +""" +function AtomicIAdd( + pointer::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicIAdd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicIDecrement` + +1) load through Pointer to get an Original Value, + +2) get a New Value through integer subtraction of 1 from Original Value, +and + +3) store the New Value back through Pointer. + +The instruction’s result is the Original Value. + +Result Type must be an integer type scalar. The type of the value +pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicIDecrement %pointer : + !spirv.ptr +``` +""" +function AtomicIDecrement( + pointer::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicIDecrement", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicIIncrement` + +1) load through Pointer to get an Original Value, + +2) get a New Value through integer addition of 1 to Original Value, and + +3) store the New Value back through Pointer. + +The instruction’s result is the Original Value. + +Result Type must be an integer type scalar. The type of the value +pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicIncrement %pointer : + !spirv.ptr +``` +""" +function AtomicIIncrement( + pointer::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicIIncrement", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicISub` + +1) load through Pointer to get an Original Value, + +2) get a New Value by integer subtraction of Value from Original Value, +and + +3) store the New Value back through Pointer. + +The instruction’s result is the Original Value. + +Result Type must be an integer type scalar. + + The type of Value must be the same as Result Type. The type of the +value pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicISub %pointer, %value : + !spirv.ptr +``` +""" +function AtomicISub( + pointer::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicISub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicOr` + +1) load through Pointer to get an Original Value, + +2) get a New Value by the bitwise OR of Original Value and Value, and + +3) store the New Value back through Pointer. + +The instruction’s result is the Original Value. + +Result Type must be an integer type scalar. + + The type of Value must be the same as Result Type. The type of the +value pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicOr %pointer, %value : + !spirv.ptr +``` +""" +function AtomicOr( + pointer::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicOr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicSMax` + +1) load through Pointer to get an Original Value, + +2) get a New Value by finding the largest signed integer of Original +Value and Value, and + +3) store the New Value back through Pointer. + +The instruction’s result is the Original Value. + +Result Type must be an integer type scalar. + + The type of Value must be the same as Result Type. The type of the +value pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicSMax %pointer, %value : + !spirv.ptr +``` +""" +function AtomicSMax( + pointer::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicSMax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicSMin` + +1) load through Pointer to get an Original Value, + +2) get a New Value by finding the smallest signed integer of Original +Value and Value, and + +3) store the New Value back through Pointer. + +The instruction’s result is the Original Value. + +Result Type must be an integer type scalar. + + The type of Value must be the same as Result Type. The type of the +value pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicSMin %pointer, %value : + !spirv.ptr +``` +""" +function AtomicSMin( + pointer::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicSMin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicUMax` + +1) load through Pointer to get an Original Value, + +2) get a New Value by finding the largest unsigned integer of Original +Value and Value, and + +3) store the New Value back through Pointer. + +The instruction’s result is the Original Value. + +Result Type must be an integer type scalar. + + The type of Value must be the same as Result Type. The type of the +value pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicUMax %pointer, %value : + !spirv.ptr +``` +""" +function AtomicUMax( + pointer::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicUMax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicUMin` + +1) load through Pointer to get an Original Value, + +2) get a New Value by finding the smallest unsigned integer of Original +Value and Value, and + +3) store the New Value back through Pointer. + +The instruction’s result is the Original Value. + +Result Type must be an integer type scalar. + + The type of Value must be the same as Result Type. The type of the +value pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicUMin %pointer, %value : + !spirv.ptr +``` +""" +function AtomicUMin( + pointer::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicUMin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`AtomicXor` + +1) load through Pointer to get an Original Value, + +2) get a New Value by the bitwise exclusive OR of Original Value and +Value, and + +3) store the New Value back through Pointer. + +The instruction’s result is the Original Value. + +Result Type must be an integer type scalar. + + The type of Value must be the same as Result Type. The type of the +value pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + + + +#### Example: + +```mlir +%0 = spirv.AtomicXor %pointer, %value : + !spirv.ptr +``` +""" +function AtomicXor( + pointer::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.AtomicXor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`BitCount` + +Results are computed per component. + +Result Type must be a scalar or vector of integer type. The components +must be wide enough to hold the unsigned Width of Base as an unsigned +value. That is, no sign bit is needed or counted when checking for a +wide enough result width. + +Base must be a scalar or vector of integer type. It must have the same +number of components as Result Type. + +The result is the unsigned value that is the number of bits in Base that +are 1. + +#### Example: + +```mlir +%2 = spirv.BitCount %0: i32 +%3 = spirv.BitCount %1: vector<4xi32> +``` +""" +function BitCount( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.BitCount", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`BitFieldInsert` + +Results are computed per component. + +Result Type must be a scalar or vector of integer type. + +The type of Base and Insert must be the same as Result Type. + +Any result bits numbered outside [Offset, Offset + Count - 1] +(inclusive) will come from the corresponding bits in Base. + +Any result bits numbered in [Offset, Offset + Count - 1] come, in +order, from the bits numbered [0, Count - 1] of Insert. + +Count must be an integer type scalar. Count is the number of bits taken +from Insert. It will be consumed as an unsigned value. Count can be 0, +in which case the result will be Base. + +Offset must be an integer type scalar. Offset is the lowest-order bit +of the bit field. It will be consumed as an unsigned value. + +The resulting value is undefined if Count or Offset or their sum is +greater than the number of bits in the result. + +#### Example: + +```mlir +%0 = spirv.BitFieldInsert %base, %insert, %offset, %count : vector<3xi32>, i8, i8 +``` +""" +function BitFieldInsert( + base::Value, + insert::Value, + offset::Value, + count::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[base, insert, offset, count] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.BitFieldInsert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`BitFieldSExtract` + +Results are computed per component. + +Result Type must be a scalar or vector of integer type. + +The type of Base must be the same as Result Type. + +If Count is greater than 0: The bits of Base numbered in [Offset, Offset ++ Count - 1] (inclusive) become the bits numbered [0, Count - 1] of the +result. The remaining bits of the result will all be the same as bit +Offset + Count - 1 of Base. + +Count must be an integer type scalar. Count is the number of bits +extracted from Base. It will be consumed as an unsigned value. Count can +be 0, in which case the result will be 0. + +Offset must be an integer type scalar. Offset is the lowest-order bit +of the bit field to extract from Base. It will be consumed as an +unsigned value. + +The resulting value is undefined if Count or Offset or their sum is +greater than the number of bits in the result. + +#### Example: + +```mlir +%0 = spirv.BitFieldSExtract %base, %offset, %count : vector<3xi32>, i8, i8 +``` +""" +function BitFieldSExtract( + base::Value, + offset::Value, + count::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[base, offset, count] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.BitFieldSExtract", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`BitFieldUExtract` + +The semantics are the same as with OpBitFieldSExtract with the exception +that there is no sign extension. The remaining bits of the result will +all be 0. + +#### Example: + +```mlir +%0 = spirv.BitFieldUExtract %base, %offset, %count : vector<3xi32>, i8, i8 +``` +""" +function BitFieldUExtract( + base::Value, + offset::Value, + count::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[base, offset, count] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.BitFieldUExtract", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`BitReverse` + +Results are computed per component. + +Result Type must be a scalar or vector of integer type. + +The type of Base must be the same as Result Type. + +The bit-number n of the result will be taken from bit-number Width - 1 - +n of Base, where Width is the OpTypeInt operand of the Result Type. + +#### Example: + +```mlir +%2 = spirv.BitReverse %0 : i32 +%3 = spirv.BitReverse %1 : vector<4xi32> +``` +""" +function BitReverse( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.BitReverse", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`Bitcast` + +Result Type must be an OpTypePointer, or a scalar or vector of +numerical-type. + +Operand must have a type of OpTypePointer, or a scalar or vector of +numerical-type. It must be a different type than Result Type. + +If either Result Type or Operand is a pointer, the other must be a +pointer (diverges from the SPIR-V spec). + +If Result Type has a different number of components than Operand, the +total number of bits in Result Type must equal the total number of bits +in Operand. Let L be the type, either Result Type or Operand\'s type, +that has the larger number of components. Let S be the other type, with +the smaller number of components. The number of components in L must be +an integer multiple of the number of components in S. The first +component (that is, the only or lowest-numbered component) of S maps to +the first components of L, and so on, up to the last component of S +mapping to the last components of L. Within this mapping, any single +component of S (mapping to multiple components of L) maps its lower- +ordered bits to the lower-numbered components of L. + +#### Example: + +```mlir +%1 = spirv.Bitcast %0 : f32 to i32 +%1 = spirv.Bitcast %0 : vector<2xf32> to i64 +%1 = spirv.Bitcast %0 : !spirv.ptr to !spirv.ptr +``` +""" +function Bitcast(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.Bitcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`BitwiseAnd` + +Results are computed per component, and within each component, per bit. + +Result Type must be a scalar or vector of integer type. The type of +Operand 1 and Operand 2 must be a scalar or vector of integer type. +They must have the same number of components as Result Type. They must +have the same component width as Result Type. + +#### Example: + +```mlir +%2 = spirv.BitwiseAnd %0, %1 : i32 +%2 = spirv.BitwiseAnd %0, %1 : vector<4xi32> +``` +""" +function BitwiseAnd( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.BitwiseAnd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`BitwiseOr` + +Results are computed per component, and within each component, per bit. + + Result Type must be a scalar or vector of integer type. The type of + Operand 1 and Operand 2 must be a scalar or vector of integer type. + They must have the same number of components as Result Type. They must + have the same component width as Result Type. + + #### Example: + + ```mlir + %2 = spirv.BitwiseOr %0, %1 : i32 + %2 = spirv.BitwiseOr %0, %1 : vector<4xi32> + ``` +""" +function BitwiseOr( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.BitwiseOr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`BitwiseXor` + +Results are computed per component, and within each component, per bit. + +Result Type must be a scalar or vector of integer type. The type of +Operand 1 and Operand 2 must be a scalar or vector of integer type. +They must have the same number of components as Result Type. They must +have the same component width as Result Type. + +#### Example: + +```mlir +%2 = spirv.BitwiseXor %0, %1 : i32 +%2 = spirv.BitwiseXor %0, %1 : vector<4xi32> +``` +""" +function BitwiseXor( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.BitwiseXor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`BranchConditional` + +Condition must be a Boolean type scalar. + +Branch weights are unsigned 32-bit integer literals. There must be +either no Branch Weights or exactly two branch weights. If present, the +first is the weight for branching to True Label, and the second is the +weight for branching to False Label. The implied probability that a +branch is taken is its weight divided by the sum of the two Branch +weights. At least one weight must be non-zero. A weight of zero does not +imply a branch is dead or permit its removal; branch weights are only +hints. The two weights must not overflow a 32-bit unsigned integer when +added together. + +This instruction must be the last instruction in a block. + + + +``` +branch-conditional-op ::= `spirv.BranchConditional` ssa-use + (`[` integer-literal, integer-literal `]`)? + `,` successor `,` successor +successor ::= bb-id branch-use-list? +branch-use-list ::= `(` ssa-use-list `:` type-list-no-parens `)` +``` + +#### Example: + +```mlir +spirv.BranchConditional %condition, ^true_branch, ^false_branch +spirv.BranchConditional %condition, ^true_branch(%0: i32), ^false_branch(%1: i32) +``` +""" +function BranchConditional( + condition::Value, + trueTargetOperands::Vector{Value}, + falseTargetOperands::Vector{Value}; + branch_weights=nothing, + trueTarget::Block, + falseTarget::Block, + location=Location(), +) + _results = IR.Type[] + _operands = Value[condition, trueTargetOperands..., falseTargetOperands...] + _owned_regions = Region[] + _successors = Block[trueTarget, falseTarget] + _attributes = NamedAttribute[] + push!( + _attributes, + operandsegmentsizes([1, length(trueTargetOperands), length(falseTargetOperands)]), + ) + !isnothing(branch_weights) && + push!(_attributes, namedattribute("branch_weights", branch_weights)) + + return IR.create_operation( + "spirv.BranchConditional", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Branch` + +This instruction must be the last instruction in a block. + +#### Example: + +```mlir +spirv.Branch ^target +spirv.Branch ^target(%0, %1: i32, f32) +``` +""" +function Branch(targetOperands::Vector{Value}; target::Block, location=Location()) + _results = IR.Type[] + _operands = Value[targetOperands...,] + _owned_regions = Region[] + _successors = Block[target,] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.Branch", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`CL_acos` + +Result is an angle in radians. + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.acos %0 : f32 +%3 = spirv.CL.acos %1 : vector<4xf16> +``` +""" +function CL_acos( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.acos", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_acosh` + +Result is an angle in radians. + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.acosh %0 : f32 +%3 = spirv.CL.acosh %1 : vector<4xf16> +``` +""" +function CL_acosh( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.acosh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_asin` + +Result is an angle in radians. + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.asin %0 : f32 +%3 = spirv.CL.asin %1 : vector<4xf16> +``` +""" +function CL_asin( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.asin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_asinh` + +Result is an angle in radians. + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.asinh %0 : f32 +%3 = spirv.CL.asinh %1 : vector<4xf16> +``` +""" +function CL_asinh( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.asinh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_atan2` + +Result is an angle in radians. + +Result Type, y and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.atan2 %0, %1 : f32 +%3 = spirv.CL.atan2 %0, %1 : vector<4xf16> +``` +""" +function CL_atan2( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.atan2", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_atan` + +Result is an angle in radians. + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.atan %0 : f32 +%3 = spirv.CL.atan %1 : vector<4xf16> +``` +""" +function CL_atan( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.atan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_atanh` + +Result is an angle in radians. + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.atanh %0 : f32 +%3 = spirv.CL.atanh %1 : vector<4xf16> +``` +""" +function CL_atanh( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.atanh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_ceil` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.ceil %0 : f32 +%3 = spirv.CL.ceil %1 : vector<3xf16> +``` +""" +function CL_ceil( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.ceil", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_cos` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.cos %0 : f32 +%3 = spirv.CL.cos %1 : vector<3xf16> +``` +""" +function CL_cos(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.cos", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_cosh` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.cosh %0 : f32 +%3 = spirv.CL.cosh %1 : vector<4xf16> +``` +""" +function CL_cosh( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.cosh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_erf` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.erf %0 : f32 +%3 = spirv.CL.erf %1 : vector<3xf16> +``` +""" +function CL_erf(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.erf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_exp` + +Compute the base-e exponential of x. (i.e. ex) + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, +must be of the same type. + +#### Example: + +```mlir +%2 = spirv.CL.exp %0 : f32 +%3 = spirv.CL.exp %1 : vector<3xf16> +``` +""" +function CL_exp(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.exp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_fabs` + +Compute the absolute value of x. + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, +must be of the same type. + +#### Example: + +```mlir +%2 = spirv.CL.fabs %0 : f32 +%3 = spirv.CL.fabs %1 : vector<3xf16> +``` +""" +function CL_fabs( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.fabs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_fmax` + +Returns y if x < y, otherwise it returns x. If one argument is a NaN, +Fmax returns the other argument. If both arguments are NaNs, Fmax returns a NaN. + +Result Type, x and y must be floating-point or vector(2,3,4,8,16) +of floating-point values. + +All of the operands, including the Result Type operand, +must be of the same type. + +#### Example: + +```mlir +%2 = spirv.CL.fmax %0, %1 : f32 +%3 = spirv.CL.fmax %0, %1 : vector<3xf16> +``` +""" +function CL_fmax( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.fmax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_fmin` + +Returns y if y < x, otherwise it returns x. If one argument is a NaN, Fmin returns the other argument. +If both arguments are NaNs, Fmin returns a NaN. + +Result Type,x and y must be floating-point or vector(2,3,4,8,16) of floating-point values. + +All of the operands, including the Result Type operand, must be of the same type. + +#### Example: + +```mlir +%2 = spirv.CL.fmin %0, %1 : f32 +%3 = spirv.CL.fmin %0, %1 : vector<3xf16> +``` +""" +function CL_fmin( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.fmin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_floor` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.floor %0 : f32 +%3 = spirv.CL.floor %1 : vector<3xf16> +``` +""" +function CL_floor( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.floor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_fma` + +Result Type, a, b and c must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%0 = spirv.CL.fma %a, %b, %c : f32 +%1 = spirv.CL.fma %a, %b, %c : vector<3xf16> +``` +""" +function CL_fma( + x::Value, + y::Value, + z::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x, y, z] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.fma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_log` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.log %0 : f32 +%3 = spirv.CL.log %1 : vector<3xf16> +``` +""" +function CL_log(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.log", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_mix` + +Result Type, x, y and a must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +Note: This instruction can be implemented using contractions such as mad +or fma. + +#### Example: + +```mlir +%0 = spirv.CL.mix %a, %b, %c : f32 +%1 = spirv.CL.mix %a, %b, %c : vector<3xf16> +``` +""" +function CL_mix( + x::Value, + y::Value, + z::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x, y, z] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.mix", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_pow` + +Result Type, x and y must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.pow %0, %1 : f32 +%3 = spirv.CL.pow %0, %1 : vector<3xf16> +``` +""" +function CL_pow( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.pow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_printf` + +printf returns 0 if it was executed successfully and -1 otherwise. + +Result Type must be i32. + +Format must be a pointer(constant) to i8. If there are insufficient +arguments for the format, the behavior is undefined. If the format +is exhausted while arguments remain, the excess arguments are evaluated +(as always) but are otherwise ignored. The printf instruction returns +when the end of the format string is encountered. + + + +#### Example: + +```mlir +%0 = spirv.CL.printf %0 %1 %2 : (!spirv.ptr, (i32, i32)) -> i32 +``` +""" +function CL_printf( + format::Value, arguments::Vector{Value}; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[format, arguments...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.CL.printf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`CL_rint` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%0 = spirv.CL.rint %0 : f32 +%1 = spirv.CL.rint %1 : vector<3xf16> +``` +""" +function CL_rint( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.rint", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_round` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.round %0 : f32 +%3 = spirv.CL.round %0 : vector<3xf16> +``` +""" +function CL_round( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.round", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_rsqrt` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.rsqrt %0 : f32 +%3 = spirv.CL.rsqrt %1 : vector<3xf16> +``` +""" +function CL_rsqrt( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.rsqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_s_abs` + +Returns |x|, where x is treated as signed integer. + +Result Type and x must be integer or vector(2,3,4,8,16) of +integer values. + +All of the operands, including the Result Type operand, +must be of the same type. + +#### Example: + +```mlir +%2 = spirv.CL.s_abs %0 : i32 +%3 = spirv.CL.s_abs %1 : vector<3xi16> +``` +""" +function CL_s_abs( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.s_abs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_s_max` + +Returns y if x < y, otherwise it returns x, where x and y are treated as signed integers. + +Result Type,x and y must be integer or vector(2,3,4,8,16) of integer values. + +All of the operands, including the Result Type operand, must be of the same type. + +#### Example: + +```mlir +%2 = spirv.CL.s_max %0, %1 : i32 +%3 = spirv.CL.s_max %0, %1 : vector<3xi16> +``` +""" +function CL_s_max( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.s_max", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_s_min` + +Returns y if x < y, otherwise it returns x, where x and y are treated as signed integers. + +Result Type,x and y must be integer or vector(2,3,4,8,16) of integer values. + +All of the operands, including the Result Type operand, must be of the same type. + +#### Example: + +```mlir +%2 = spirv.CL.s_min %0, %1 : i32 +%3 = spirv.CL.s_min %0, %1 : vector<3xi16> +``` +""" +function CL_s_min( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.s_min", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_sin` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.sin %0 : f32 +%3 = spirv.CL.sin %1 : vector<3xf16> +``` +""" +function CL_sin(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.sin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_sinh` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.sinh %0 : f32 +%3 = spirv.CL.sinh %1 : vector<4xf16> +``` +""" +function CL_sinh( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.sinh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_sqrt` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.sqrt %0 : f32 +%3 = spirv.CL.sqrt %1 : vector<3xf16> +``` +""" +function CL_sqrt( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.sqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_tan` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.tan %0 : f32 +%3 = spirv.CL.tan %1 : vector<4xf16> +``` +""" +function CL_tan(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.tan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_tanh` + +Result Type and x must be floating-point or vector(2,3,4,8,16) of +floating-point values. + +All of the operands, including the Result Type operand, must be of the +same type. + +#### Example: + +```mlir +%2 = spirv.CL.tanh %0 : f32 +%3 = spirv.CL.tanh %1 : vector<3xf16> +``` +""" +function CL_tanh( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.tanh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_u_max` + +Returns y if x < y, otherwise it returns x, where x and y are treated as unsigned integers. + +Result Type,x and y must be integer or vector(2,3,4,8,16) of integer values. + +All of the operands, including the Result Type operand, must be of the same type. + +#### Example: + +```mlir +%2 = spirv.CL.u_max %0, %1 : i32 +%3 = spirv.CL.u_max %0, %1 : vector<3xi16> +``` +""" +function CL_u_max( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.u_max", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CL_u_min` + +Returns y if x < y, otherwise it returns x, where x and y are treated as unsigned integers. + +Result Type,x and y must be integer or vector(2,3,4,8,16) of integer values. + +All of the operands, including the Result Type operand, must be of the same type. + +#### Example: + +```mlir +%2 = spirv.CL.u_min %0, %1 : i32 +%3 = spirv.CL.u_min %0, %1 : vector<3xi16> +``` +""" +function CL_u_min( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.CL.u_min", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`CompositeConstruct` + +Result Type must be a composite type, whose top-level +members/elements/components/columns have the same type as the types of +the operands, with one exception. The exception is that for constructing +a vector, the operands may also be vectors with the same component type +as the Result Type component type. When constructing a vector, the total +number of components in all the operands must equal the number of +components in Result Type. + +Constituents will become members of a structure, or elements of an +array, or components of a vector, or columns of a matrix. There must be +exactly one Constituent for each top-level +member/element/component/column of the result, with one exception. The +exception is that for constructing a vector, a contiguous subset of the +scalars consumed can be represented by a vector operand instead. The +Constituents must appear in the order needed by the definition of the +type of the result. When constructing a vector, there must be at least +two Constituent operands. + +#### Example: + +```mlir +%a = spirv.CompositeConstruct %1, %2, %3 : vector<3xf32> +%b = spirv.CompositeConstruct %a, %1 : (vector<3xf32>, f32) -> vector<4xf32> + +%c = spirv.CompositeConstruct %1 : + (f32) -> !spirv.coopmatrix<4x4xf32, Subgroup, MatrixA> + +%d = spirv.CompositeConstruct %a, %4, %5 : + (vector<3xf32>, !spirv.array<4xf32>, !spirv.struct<(f32)>) -> + !spirv.struct<(vector<3xf32>, !spirv.array<4xf32>, !spirv.struct<(f32)>)> +``` +""" +function CompositeConstruct( + constituents::Vector{Value}; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[constituents...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.CompositeConstruct", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`CompositeExtract` + +Result Type must be the type of object selected by the last provided +index. The instruction result is the extracted object. + +Composite is the composite to extract from. + +Indexes walk the type hierarchy, potentially down to component +granularity, to select the part to extract. All indexes must be in +bounds. All composite constituents use zero-based numbering, as +described by their OpType… instruction. + + + +``` +composite-extract-op ::= ssa-id `=` `spirv.CompositeExtract` ssa-use + `[` integer-literal (\',\' integer-literal)* `]` + `:` composite-type +``` + +#### Example: + +```mlir +%0 = spirv.Variable : !spirv.ptr>, Function> +%1 = spirv.Load \"Function\" %0 [\"Volatile\"] : !spirv.array<4x!spirv.array<4xf32>> +%2 = spirv.CompositeExtract %1[1 : i32] : !spirv.array<4x!spirv.array<4xf32>> +``` +""" +function CompositeExtract( + composite::Value; component::IR.Type, indices, location=Location() +) + _results = IR.Type[component,] + _operands = Value[composite,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("indices", indices),] + + return IR.create_operation( + "spirv.CompositeExtract", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`CompositeInsert` + +Result Type must be the same type as Composite. + +Object is the object to use as the modified part. + +Composite is the composite to copy all but the modified part from. + +Indexes walk the type hierarchy of Composite to the desired depth, +potentially down to component granularity, to select the part to modify. +All indexes must be in bounds. All composite constituents use zero-based +numbering, as described by their OpType… instruction. The type of the +part selected to modify must match the type of Object. + + + +``` +composite-insert-op ::= ssa-id `=` `spirv.CompositeInsert` ssa-use, ssa-use + `[` integer-literal (\',\' integer-literal)* `]` + `:` object-type `into` composite-type +``` + +#### Example: + +```mlir +%0 = spirv.CompositeInsert %object, %composite[1 : i32] : f32 into !spirv.array<4xf32> +``` +""" +function CompositeInsert( + object::Value, composite::Value; result::IR.Type, indices, location=Location() +) + _results = IR.Type[result,] + _operands = Value[object, composite] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("indices", indices),] + + return IR.create_operation( + "spirv.CompositeInsert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Constant` + +This op declares a SPIR-V normal constant. SPIR-V has multiple constant +instructions covering different constant types: + +* `OpConstantTrue` and `OpConstantFalse` for boolean constants +* `OpConstant` for scalar constants +* `OpConstantComposite` for composite constants +* `OpConstantNull` for null constants +* ... + +Having such a plethora of constant instructions renders IR transformations +more tedious. Therefore, we use a single `spirv.Constant` op to represent +them all. Note that conversion between those SPIR-V constant instructions +and this op is purely mechanical; so it can be scoped to the binary +(de)serialization process. + + + +``` +spirv.Constant-op ::= ssa-id `=` `spirv.Constant` attribute-value + (`:` spirv-type)? +``` + +#### Example: + +```mlir +%0 = spirv.Constant true +%1 = spirv.Constant dense<[2, 3]> : vector<2xf32> +%2 = spirv.Constant [dense<3.0> : vector<2xf32>] : !spirv.array<1xvector<2xf32>> +``` + +TODO: support constant structs +""" +function Constant(; constant::IR.Type, value, location=Location()) + _results = IR.Type[constant,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "spirv.Constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ControlBarrier` + +All invocations of this module within Execution scope must reach this +point of execution before any invocation will proceed beyond it. + +When Execution is Workgroup or larger, behavior is undefined if this +instruction is used in control flow that is non-uniform within +Execution. When Execution is Subgroup or Invocation, the behavior of +this instruction in non-uniform control flow is defined by the client +API. + +If Semantics is not None, this instruction also serves as an +OpMemoryBarrier instruction, and must also perform and adhere to the +description and semantics of an OpMemoryBarrier instruction with the +same Memory and Semantics operands. This allows atomically specifying +both a control barrier and a memory barrier (that is, without needing +two instructions). If Semantics is None, Memory is ignored. + +Before version 1.3, it is only valid to use this instruction with +TessellationControl, GLCompute, or Kernel execution models. There is no +such restriction starting with version 1.3. + +When used with the TessellationControl execution model, it also +implicitly synchronizes the Output Storage Class: Writes to Output +variables performed by any invocation executed prior to a +OpControlBarrier will be visible to any other invocation after return +from that OpControlBarrier. + +#### Example: + +```mlir +spirv.ControlBarrier \"Workgroup\", \"Device\", \"Acquire|UniformMemory\" +``` +""" +function ControlBarrier(; + execution_scope, memory_scope, memory_semantics, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("memory_scope", memory_scope), + namedattribute("memory_semantics", memory_semantics), + ] + + return IR.create_operation( + "spirv.ControlBarrier", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ConvertFToS` + +Result Type must be a scalar or vector of integer type. + +Float Value must be a scalar or vector of floating-point type. It must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%1 = spirv.ConvertFToS %0 : f32 to i32 +%3 = spirv.ConvertFToS %2 : vector<3xf32> to vector<3xi32> +``` +""" +function ConvertFToS(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.ConvertFToS", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ConvertFToU` + +Result Type must be a scalar or vector of integer type, whose Signedness +operand is 0. + +Float Value must be a scalar or vector of floating-point type. It must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%1 = spirv.ConvertFToU %0 : f32 to i32 +%3 = spirv.ConvertFToU %2 : vector<3xf32> to vector<3xi32> +``` +""" +function ConvertFToU(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.ConvertFToU", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ConvertPtrToU` + +Result Type must be a scalar of integer type, whose Signedness operand is 0. + +Pointer must be a physical pointer type. If the bit width of Pointer is +smaller than that of Result Type, the conversion zero extends Pointer. +If the bit width of Pointer is larger than that of Result Type, +the conversion truncates Pointer. + +For same bit width Pointer and Result Type, this is the same as OpBitcast. + +#### Example: + +```mlir +%1 = spirv.ConvertPtrToU %0 : !spirv.ptr to i32 +``` +""" +function ConvertPtrToU(pointer::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[pointer,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.ConvertPtrToU", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ConvertSToF` + +Result Type must be a scalar or vector of floating-point type. + +Signed Value must be a scalar or vector of integer type. It must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%1 = spirv.ConvertSToF %0 : i32 to f32 +%3 = spirv.ConvertSToF %2 : vector<3xi32> to vector<3xf32> +``` +""" +function ConvertSToF(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.ConvertSToF", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ConvertUToF` + +Result Type must be a scalar or vector of floating-point type. + +Unsigned Value must be a scalar or vector of integer type. It must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%1 = spirv.ConvertUToF %0 : i32 to f32 +%3 = spirv.ConvertUToF %2 : vector<3xi32> to vector<3xf32> +``` +""" +function ConvertUToF(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.ConvertUToF", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ConvertUToPtr` + +Result Type must be a physical pointer type. + +Integer Value must be a scalar of integer type, whose Signedness +operand is 0. If the bit width of Integer Value is smaller +than that of Result Type, the conversion zero extends Integer Value. +If the bit width of Integer Value is larger than that of Result Type, +the conversion truncates Integer Value. + +For same-width Integer Value and Result Type, this is the same as OpBitcast. + +#### Example: + +```mlir +%1 = spirv.ConvertUToPtr %0 : i32 to !spirv.ptr +``` +""" +function ConvertUToPtr(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.ConvertUToPtr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`CopyMemory` + +If present, any Memory Operands must begin with a memory operand +literal. If not present, it is the same as specifying the memory operand +None. Before version 1.4, at most one memory operands mask can be +provided. Starting with version 1.4 two masks can be provided, as +described in Memory Operands. If no masks or only one mask is present, +it applies to both Source and Target. If two masks are present, the +first applies to Target and cannot include MakePointerVisible, and the +second applies to Source and cannot include MakePointerAvailable. + + + +``` +copy-memory-op ::= `spirv.CopyMemory ` storage-class ssa-use + storage-class ssa-use + (`[` memory-access `]` (`, [` memory-access `]`)?)? + ` : ` spirv-element-type +``` + +#### Example: + +```mlir +%0 = spirv.Variable : !spirv.ptr +%1 = spirv.Variable : !spirv.ptr +spirv.CopyMemory \"Function\" %0, \"Function\" %1 : f32 +``` +""" +function CopyMemory( + target::Value, + source::Value; + memory_access=nothing, + alignment=nothing, + source_memory_access=nothing, + source_alignment=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[target, source] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(memory_access) && + push!(_attributes, namedattribute("memory_access", memory_access)) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + !isnothing(source_memory_access) && + push!(_attributes, namedattribute("source_memory_access", source_memory_access)) + !isnothing(source_alignment) && + push!(_attributes, namedattribute("source_alignment", source_alignment)) + + return IR.create_operation( + "spirv.CopyMemory", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Dot` + +Result Type must be a floating point scalar. + +Vector 1 and Vector 2 must be vectors of the same type, and their component +type must be Result Type. + +#### Example: + +```mlir +%0 = spirv.Dot %v1, %v2 : vector<4xf32> -> f32 +``` +""" +function Dot(vector1::Value, vector2::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[vector1, vector2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.Dot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`EXT_AtomicFAdd` + + + + + +Perform the following steps atomically with respect to any other atomic +accesses within Scope to the same location: + +1) load through Pointer to get an Original Value, + +2) get a New Value by float addition of Original Value and Value, and + +3) store the New Value back through Pointer. + +The instruction\'s result is the Original Value. + +Result Type must be a floating-point type scalar. + +The type of Value must be the same as Result Type. The type of the value +pointed to by Pointer must be the same as Result Type. + +Memory must be a valid memory Scope. + +#### Example: + +```mlir +%0 = spirv.EXT.AtomicFAdd %pointer, %value : + !spirv.ptr +``` +""" +function EXT_AtomicFAdd( + pointer::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + memory_scope, + semantics, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), namedattribute("semantics", semantics) + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.EXT.AtomicFAdd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`EntryPoint` + +Execution Model is the execution model for the entry point and its +static call tree. See Execution Model. + +Entry Point must be the Result of an OpFunction instruction. + +Name is a name string for the entry point. A module cannot have two +OpEntryPoint instructions with the same Execution Model and the same +Name string. + +Interface is a list of symbol references to `spirv.GlobalVariable` +operations. These declare the set of global variables from a +module that form the interface of this entry point. The set of +Interface symbols must be equal to or a superset of the +`spirv.GlobalVariable`s referenced by the entry point’s static call +tree, within the interface’s storage classes. Before version 1.4, +the interface’s storage classes are limited to the Input and +Output storage classes. Starting with version 1.4, the interface’s +storage classes are all storage classes used in declaring all +global variables referenced by the entry point’s call tree. + + + +``` +execution-model ::= \"Vertex\" | \"TesellationControl\" | + + +entry-point-op ::= ssa-id `=` `spirv.EntryPoint` execution-model + symbol-reference (`, ` symbol-reference)* +``` + +#### Example: + +```mlir +spirv.EntryPoint \"GLCompute\" @foo +spirv.EntryPoint \"Kernel\" @foo, @var1, @var2 + +``` +""" +function EntryPoint(; execution_model, fn, interface, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_model", execution_model), + namedattribute("fn", fn), + namedattribute("interface", interface), + ] + + return IR.create_operation( + "spirv.EntryPoint", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ExecutionMode` + +Entry Point must be the Entry Point operand of an OpEntryPoint +instruction. + +Mode is the execution mode. See Execution Mode. + +This instruction is only valid when the Mode operand is an execution +mode that takes no Extra Operands, or takes Extra Operands that are not + operands. + + + +``` +execution-mode ::= \"Invocations\" | \"SpacingEqual\" | + + +execution-mode-op ::= `spirv.ExecutionMode ` ssa-use execution-mode + (integer-literal (`, ` integer-literal)* )? +``` + +#### Example: + +```mlir +spirv.ExecutionMode @foo \"ContractionOff\" +spirv.ExecutionMode @bar \"LocalSizeHint\", 3, 4, 5 +``` +""" +function ExecutionMode(; fn, execution_mode, values, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("fn", fn), + namedattribute("execution_mode", execution_mode), + namedattribute("values", values), + ] + + return IR.create_operation( + "spirv.ExecutionMode", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`FAdd` + +Result Type must be a scalar or vector of floating-point type. + +The types of Operand 1 and Operand 2 both must be the same as Result +Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FAdd %0, %1 : f32 +%5 = spirv.FAdd %2, %3 : vector<4xf32> +``` +""" +function FAdd( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FAdd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FConvert` + +Result Type must be a scalar or vector of floating-point type. + +Float Value must be a scalar or vector of floating-point type. It must +have the same number of components as Result Type. The component width +cannot equal the component width in Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%1 = spirv.FConvertOp %0 : f32 to f64 +%3 = spirv.FConvertOp %2 : vector<3xf32> to vector<3xf64> +``` +""" +function FConvert(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.FConvert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`FDiv` + +Result Type must be a scalar or vector of floating-point type. + +The types of Operand 1 and Operand 2 both must be the same as Result +Type. + +Results are computed per component. The resulting value is undefined +if Operand 2 is 0. + +#### Example: + +```mlir +%4 = spirv.FDiv %0, %1 : f32 +%5 = spirv.FDiv %2, %3 : vector<4xf32> +``` +""" +function FDiv( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FDiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FMod` + +Result Type must be a scalar or vector of floating-point type. + +The types of Operand 1 and Operand 2 both must be the same as Result +Type. + +Results are computed per component. The resulting value is undefined +if Operand 2 is 0. Otherwise, the result is the remainder r of Operand +1 divided by Operand 2 where if r ≠ 0, the sign of r is the same as the +sign of Operand 2. + +#### Example: + +```mlir +%4 = spirv.FMod %0, %1 : f32 +%5 = spirv.FMod %2, %3 : vector<4xf32> +``` +""" +function FMod( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FMod", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FMul` + +Result Type must be a scalar or vector of floating-point type. + +The types of Operand 1 and Operand 2 both must be the same as Result +Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FMul %0, %1 : f32 +%5 = spirv.FMul %2, %3 : vector<4xf32> +``` +""" +function FMul( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FMul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FNegate` + +Result Type must be a scalar or vector of floating-point type. + +The type of Operand must be the same as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%1 = spirv.FNegate %0 : f32 +%3 = spirv.FNegate %2 : vector<4xf32> +``` +""" +function FNegate( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FNegate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FOrdEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FOrdEqual %0, %1 : f32 +%5 = spirv.FOrdEqual %2, %3 : vector<4xf32> +``` +""" +function FOrdEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FOrdEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FOrdGreaterThanEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FOrdGreaterThanEqual %0, %1 : f32 +%5 = spirv.FOrdGreaterThanEqual %2, %3 : vector<4xf32> +``` +""" +function FOrdGreaterThanEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FOrdGreaterThanEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FOrdGreaterThan` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FOrdGreaterThan %0, %1 : f32 +%5 = spirv.FOrdGreaterThan %2, %3 : vector<4xf32> +``` +""" +function FOrdGreaterThan( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FOrdGreaterThan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FOrdLessThanEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FOrdLessThanEqual %0, %1 : f32 +%5 = spirv.FOrdLessThanEqual %2, %3 : vector<4xf32> +``` +""" +function FOrdLessThanEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FOrdLessThanEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FOrdLessThan` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FOrdLessThan %0, %1 : f32 +%5 = spirv.FOrdLessThan %2, %3 : vector<4xf32> +``` +""" +function FOrdLessThan( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FOrdLessThan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FOrdNotEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FOrdNotEqual %0, %1 : f32 +%5 = spirv.FOrdNotEqual %2, %3 : vector<4xf32> +``` +""" +function FOrdNotEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FOrdNotEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FRem` + +Result Type must be a scalar or vector of floating-point type. + +The types of Operand 1 and Operand 2 both must be the same as Result +Type. + +Results are computed per component. The resulting value is undefined +if Operand 2 is 0. Otherwise, the result is the remainder r of Operand +1 divided by Operand 2 where if r ≠ 0, the sign of r is the same as the +sign of Operand 1. + +#### Example: + +```mlir +%4 = spirv.FRemOp %0, %1 : f32 +%5 = spirv.FRemOp %2, %3 : vector<4xf32> +``` +""" +function FRem( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FRem", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FSub` + +Result Type must be a scalar or vector of floating-point type. + +The types of Operand 1 and Operand 2 both must be the same as Result +Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FRemOp %0, %1 : f32 +%5 = spirv.FRemOp %2, %3 : vector<4xf32> +``` +""" +function FSub( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FSub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FUnordEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FUnordEqual %0, %1 : f32 +%5 = spirv.FUnordEqual %2, %3 : vector<4xf32> +``` +""" +function FUnordEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FUnordEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FUnordGreaterThanEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FUnordGreaterThanEqual %0, %1 : f32 +%5 = spirv.FUnordGreaterThanEqual %2, %3 : vector<4xf32> +``` +""" +function FUnordGreaterThanEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FUnordGreaterThanEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FUnordGreaterThan` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FUnordGreaterThan %0, %1 : f32 +%5 = spirv.FUnordGreaterThan %2, %3 : vector<4xf32> +``` +""" +function FUnordGreaterThan( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FUnordGreaterThan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FUnordLessThanEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FUnordLessThanEqual %0, %1 : f32 +%5 = spirv.FUnordLessThanEqual %2, %3 : vector<4xf32> +``` +""" +function FUnordLessThanEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FUnordLessThanEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FUnordLessThan` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FUnordLessThan %0, %1 : f32 +%5 = spirv.FUnordLessThan %2, %3 : vector<4xf32> +``` +""" +function FUnordLessThan( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FUnordLessThan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`FUnordNotEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +floating-point type. They must have the same type, and they must have +the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.FUnordNotEqual %0, %1 : f32 +%5 = spirv.FUnordNotEqual %2, %3 : vector<4xf32> +``` +""" +function FUnordNotEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.FUnordNotEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`func` + +This op declares or defines a SPIR-V function using one region, which +contains one or more blocks. + +Different from the SPIR-V binary format, this op is not allowed to +implicitly capture global values, and all external references must use +function arguments or symbol references. This op itself defines a symbol +that is unique in the enclosing module op. + +This op itself takes no operands and generates no results. Its region +can take zero or more arguments and return zero or one values. + +From `SPV_KHR_physical_storage_buffer`: +If a parameter of function is +- a pointer (or contains a pointer) in the PhysicalStorageBuffer storage + class, the function parameter must be decorated with exactly one of + `Aliased` or `Restrict`. +- a pointer (or contains a pointer) and the type it points to is a pointer + in the PhysicalStorageBuffer storage class, the function parameter must + be decorated with exactly one of `AliasedPointer` or `RestrictPointer`. + + + +``` +spv-function-control ::= \"None\" | \"Inline\" | \"DontInline\" | ... +spv-function-op ::= `spirv.func` function-signature + spv-function-control region +``` + +#### Example: + +```mlir +spirv.func @foo() -> () \"None\" { ... } +spirv.func @bar() -> () \"Inline|Pure\" { ... } + +spirv.func @aliased_pointer(%arg0: !spirv.ptr, + { spirv.decoration = #spirv.decoration }) -> () \"None\" { ... } + +spirv.func @restrict_pointer(%arg0: !spirv.ptr, + { spirv.decoration = #spirv.decoration }) -> () \"None\" { ... } + +spirv.func @aliased_pointee(%arg0: !spirv.ptr, Generic> { spirv.decoration = + #spirv.decoration }) -> () \"None\" { ... } + +spirv.func @restrict_pointee(%arg0: !spirv.ptr, Generic> { spirv.decoration = + #spirv.decoration }) -> () \"None\" { ... } +``` +""" +function func(; + function_type, + arg_attrs=nothing, + res_attrs=nothing, + sym_name, + function_control, + linkage_attributes=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("function_type", function_type), + namedattribute("sym_name", sym_name), + namedattribute("function_control", function_control), + ] + !isnothing(arg_attrs) && push!(_attributes, namedattribute("arg_attrs", arg_attrs)) + !isnothing(res_attrs) && push!(_attributes, namedattribute("res_attrs", res_attrs)) + !isnothing(linkage_attributes) && + push!(_attributes, namedattribute("linkage_attributes", linkage_attributes)) + + return IR.create_operation( + "spirv.func", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`FunctionCall` + +Result Type is the type of the return value of the function. It must be +the same as the Return Type operand of the Function Type operand of the +Function operand. + +Function is an OpFunction instruction. This could be a forward +reference. + +Argument N is the object to copy to parameter N of Function. + +Note: A forward call is possible because there is no missing type +information: Result Type must match the Return Type of the function, and +the calling argument types must match the formal parameter types. + +#### Example: + +```mlir +spirv.FunctionCall @f_void(%arg0) : (i32) -> () +%0 = spirv.FunctionCall @f_iadd(%arg0, %arg1) : (i32, i32) -> i32 +``` +""" +function FunctionCall( + arguments::Vector{Value}; + return_value=nothing::Union{Nothing,IR.Type}, + callee, + location=Location(), +) + _results = IR.Type[] + _operands = Value[arguments...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("callee", callee),] + !isnothing(return_value) && push!(_results, return_value) + + return IR.create_operation( + "spirv.FunctionCall", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GL_Acos` + +The standard trigonometric arc cosine of x radians. + +Result is an angle, in radians, whose cosine is x. The range of result +values is [0, π]. Result is undefined if abs x > 1. + +The operand x must be a scalar or vector whose component type is 16-bit or +32-bit floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Acos %0 : f32 +%3 = spirv.GL.Acos %1 : vector<3xf16> +``` +""" +function GL_Acos( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Acos", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Asin` + +The standard trigonometric arc sine of x radians. + +Result is an angle, in radians, whose sine is x. The range of result values +is [-π / 2, π / 2]. Result is undefined if abs x > 1. + +The operand x must be a scalar or vector whose component type is 16-bit or +32-bit floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Asin %0 : f32 +%3 = spirv.GL.Asin %1 : vector<3xf16> +``` +""" +function GL_Asin( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Asin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Atan` + +The standard trigonometric arc tangent of x radians. + +Result is an angle, in radians, whose tangent is y_over_x. The range of +result values is [-π / 2, π / 2]. + +The operand x must be a scalar or vector whose component type is 16-bit or +32-bit floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Atan %0 : f32 +%3 = spirv.GL.Atan %1 : vector<3xf16> +``` +""" +function GL_Atan( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Atan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Ceil` + +Result is the value equal to the nearest whole number that is greater than +or equal to x. + +The operand x must be a scalar or vector whose component type is +floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Ceil %0 : f32 +%3 = spirv.GL.Ceil %1 : vector<3xf16> +``` +""" +function GL_Ceil( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Ceil", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Cos` + +The standard trigonometric cosine of x radians. + +The operand x must be a scalar or vector whose component type is 16-bit or +32-bit floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Cos %0 : f32 +%3 = spirv.GL.Cos %1 : vector<3xf16> +``` +""" +function GL_Cos(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Cos", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Cosh` + +Hyperbolic cosine of x radians. + +The operand x must be a scalar or vector whose component type is 16-bit or +32-bit floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Cosh %0 : f32 +%3 = spirv.GL.Cosh %1 : vector<3xf16> +``` +""" +function GL_Cosh( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Cosh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Exp` + +Result is the natural exponentiation of x; e^x. + +The operand x must be a scalar or vector whose component type is +16-bit or 32-bit floating-point. + +Result Type and the type of x must be the same type. Results are +computed per component.\"; + +#### Example: + +```mlir +%2 = spirv.GL.Exp %0 : f32 +%3 = spirv.GL.Exp %1 : vector<3xf16> +``` +""" +function GL_Exp(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Exp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_FAbs` + +Result is x if x >= 0; otherwise result is -x. + +The operand x must be a scalar or vector whose component type is +floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.FAbs %0 : f32 +%3 = spirv.GL.FAbs %1 : vector<3xf16> +``` +""" +function GL_FAbs( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.FAbs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_FClamp` + +Result is min(max(x, minVal), maxVal). The resulting value is undefined if +minVal > maxVal. The semantics used by min() and max() are those of FMin and +FMax. + +The operands must all be a scalar or vector whose component type is +floating-point. + +Result Type and the type of all operands must be the same type. Results are +computed per component. + + +``` +fclamp-op ::= ssa-id `=` `spirv.GL.FClamp` ssa-use, ssa-use, ssa-use `:` + float-scalar-vector-type +``` +#### Example: + +```mlir +%2 = spirv.GL.FClamp %x, %min, %max : f32 +%3 = spirv.GL.FClamp %x, %min, %max : vector<3xf16> +``` +""" +function GL_FClamp(x::Value, y::Value, z::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[x, y, z] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.GL.FClamp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GL_FMax` + +Result is y if x < y; otherwise result is x. Which operand is the +result is undefined if one of the operands is a NaN. + +The operands must all be a scalar or vector whose component type +is floating-point. + +Result Type and the type of all operands must be the same +type. Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.GL.FMax %0, %1 : f32 +%3 = spirv.GL.FMax %0, %1 : vector<3xf16> +``` +""" +function GL_FMax( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.FMax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_FMin` + +Result is y if y < x; otherwise result is x. Which operand is the result is +undefined if one of the operands is a NaN. + +The operands must all be a scalar or vector whose component type is +floating-point. + +Result Type and the type of all operands must be the same type. Results are +computed per component. + +#### Example: + +```mlir +%2 = spirv.GL.FMin %0, %1 : f32 +%3 = spirv.GL.FMin %0, %1 : vector<3xf16> +``` +""" +function GL_FMin( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.FMin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_FMix` + +Result is the linear blend of x and y, i.e., x * (1 - a) + y * a. + +The operands must all be a scalar or vector whose component type is floating-point. + +Result Type and the type of all operands must be the same type. Results are computed per component. + + + +#### Example: + +```mlir +%0 = spirv.GL.FMix %x : f32, %y : f32, %a : f32 -> f32 +%0 = spirv.GL.FMix %x : vector<4xf32>, %y : vector<4xf32>, %a : vector<4xf32> -> vector<4xf32> +``` +""" +function GL_FMix( + x::Value, + y::Value, + a::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x, y, a] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.FMix", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_FSign` + +Result is 1.0 if x > 0, 0.0 if x = 0, or -1.0 if x < 0. + +The operand x must be a scalar or vector whose component type is +floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.FSign %0 : f32 +%3 = spirv.GL.FSign %1 : vector<3xf16> +``` +""" +function GL_FSign( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.FSign", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_FindUMsb` + +Results in the bit number of the most-significant 1-bit in the binary +representation of Value. If Value is 0, the result is -1. + +Result Type and the type of Value must both be integer scalar or +integer vector types. Result Type and operand types must have the +same number of components with the same component width. Results are +computed per component. + +This instruction is currently limited to 32-bit width components. +""" +function GL_FindUMsb( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.FindUMsb", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Floor` + +Result is the value equal to the nearest whole number that is less than or +equal to x. + +The operand x must be a scalar or vector whose component type is +floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Floor %0 : f32 +%3 = spirv.GL.Floor %1 : vector<3xf16> +``` +""" +function GL_Floor( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Floor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Fma` + +In uses where this operation is decorated with NoContraction: + +- fma is considered a single operation, whereas the expression a * b + c + is considered two operations. +- The precision of fma can differ from the precision of the expression + a * b + c. +- fma will be computed with the same precision as any other fma decorated + with NoContraction, giving invariant results for the same input values + of a, b, and c. + +Otherwise, in the absence of a NoContraction decoration, there are no +special constraints on the number of operations or difference in precision +between fma and the expression a * b +c. + +The operands must all be a scalar or vector whose component type is +floating-point. + +Result Type and the type of all operands must be the same type. Results +are computed per component. + + +``` +fma-op ::= ssa-id `=` `spirv.GL.Fma` ssa-use, ssa-use, ssa-use `:` + float-scalar-vector-type +``` +#### Example: + +```mlir +%0 = spirv.GL.Fma %a, %b, %c : f32 +%1 = spirv.GL.Fma %a, %b, %c : vector<3xf16> +``` +""" +function GL_Fma(x::Value, y::Value, z::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[x, y, z] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.GL.Fma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GL_FrexpStruct` + +Result is a structure containing x split into a floating-point significand +in the range (-1.0, 0.5] or [0.5, 1.0) and an integral exponent of 2, such that: + +x = significand * 2^exponent + +If x is a zero, the exponent is 0.0. If x is an infinity or a NaN, the +exponent is undefined. If x is 0.0, the significand is 0.0. If x is -0.0, +the significand is -0.0 + +Result Type must be an OpTypeStruct with two members. Member 0 must have +the same type as the type of x. Member 0 holds the significand. Member 1 +must be a scalar or vector with integer component type, with 32-bit +component width. Member 1 holds the exponent. These two members and x must +have the same number of components. + +The operand x must be a scalar or vector whose component type is +floating-point. + +#### Example: + +```mlir +%2 = spirv.GL.FrexpStruct %0 : f32 -> !spirv.struct +%3 = spirv.GL.FrexpStruct %0 : vector<3xf32> -> !spirv.struct, vector<3xi32>> +``` +""" +function GL_FrexpStruct(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.GL.FrexpStruct", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GL_InverseSqrt` + +Result is the reciprocal of sqrt x. Result is undefined if x <= 0. + +The operand x must be a scalar or vector whose component type is +floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.InverseSqrt %0 : f32 +%3 = spirv.GL.InverseSqrt %1 : vector<3xf16> +``` +""" +function GL_InverseSqrt( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.InverseSqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Ldexp` + +Builds a floating-point number from x and the corresponding +integral exponent of two in exp: + +significand * 2^exponent + +If this product is too large to be represented in the floating-point +type, the resulting value is undefined. If exp is greater than +128 +(single precision) or +1024 (double precision), the resulting value is +undefined. If exp is less than -126 (single precision) or -1022 (double precision), +the result may be flushed to zero. Additionally, splitting the value +into a significand and exponent using frexp and then reconstructing a +floating-point value using ldexp should yield the original input for +zero and all finite non-denormalized values. + +The operand x must be a scalar or vector whose component type is floating-point. + +The exp operand must be a scalar or vector with integer component type. +The number of components in x and exp must be the same. + +Result Type must be the same type as the type of x. Results are computed per +component. + + + +#### Example: + +```mlir +%y = spirv.GL.Ldexp %x : f32, %exp : i32 -> f32 +%y = spirv.GL.Ldexp %x : vector<3xf32>, %exp : vector<3xi32> -> vector<3xf32> +``` +""" +function GL_Ldexp( + x::Value, exp::Value; y=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[x, exp] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(y) && push!(_results, y) + + return IR.create_operation( + "spirv.GL.Ldexp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Log` + +Result is the natural logarithm of x, i.e., the value y which satisfies the +equation x = ey. Result is undefined if x <= 0. + +The operand x must be a scalar or vector whose component type is 16-bit or +32-bit floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Log %0 : f32 +%3 = spirv.GL.Log %1 : vector<3xf16> +``` +""" +function GL_Log(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Log", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Pow` + +Result is x raised to the y power; x^y. + +Result is undefined if x = 0 and y ≤ 0. + +The operand x and y must be a scalar or vector whose component type is +16-bit or 32-bit floating-point. + +Result Type and the type of all operands must be the same type. Results are +computed per component. + +#### Example: + +```mlir +%2 = spirv.GL.Pow %0, %1 : f32 +%3 = spirv.GL.Pow %0, %1 : vector<3xf16> +``` +""" +function GL_Pow( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Pow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_RoundEven` + +Result is the value equal to the nearest whole number to x. A fractional +part of 0.5 will round toward the nearest even whole number. (Both 3.5 and +4.5 for x will be 4.0.) + +The operand x must be a scalar or vector whose component type is +floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.RoundEven %0 : f32 +%3 = spirv.GL.RoundEven %1 : vector<3xf16> +``` +""" +function GL_RoundEven( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.RoundEven", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Round` + +Result is the value equal to the nearest whole number to x. The fraction +0.5 will round in a direction chosen by the implementation, presumably +the direction that is fastest. This includes the possibility that +Round x is the same value as RoundEven x for all values of x. + +The operand x must be a scalar or vector whose component type is +floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Round %0 : f32 +%3 = spirv.GL.Round %1 : vector<3xf16> +``` +""" +function GL_Round( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Round", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_SAbs` + +Result is x if x ≥ 0; otherwise result is -x, where x is interpreted as a +signed integer. + +Result Type and the type of x must both be integer scalar or integer vector +types. Result Type and operand types must have the same number of components +with the same component width. Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.GL.SAbs %0 : i32 +%3 = spirv.GL.SAbs %1 : vector<3xi16> +``` +""" +function GL_SAbs( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.SAbs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_SClamp` + +Result is min(max(x, minVal), maxVal), where x, minVal and maxVal are +interpreted as signed integers. The resulting value is undefined if +minVal > maxVal. + +Result Type and the type of the operands must both be integer scalar or +integer vector types. Result Type and operand types must have the same number +of components with the same component width. Results are computed per +component. + + +``` +uclamp-op ::= ssa-id `=` `spirv.GL.UClamp` ssa-use, ssa-use, ssa-use `:` + sgined-scalar-vector-type +``` +#### Example: + +```mlir +%2 = spirv.GL.SClamp %x, %min, %max : si32 +%3 = spirv.GL.SClamp %x, %min, %max : vector<3xsi16> +``` +""" +function GL_SClamp(x::Value, y::Value, z::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[x, y, z] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.GL.SClamp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GL_SMax` + +Result is y if x < y; otherwise result is x, where x and y are interpreted +as signed integers. + +Result Type and the type of x and y must both be integer scalar or integer +vector types. Result Type and operand types must have the same number of +components with the same component width. Results are computed per +component. + +#### Example: + +```mlir +%2 = spirv.GL.SMax %0, %1 : i32 +%3 = spirv.GL.SMax %0, %1 : vector<3xi16> +``` +""" +function GL_SMax( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.SMax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_SMin` + +Result is y if y < x; otherwise result is x, where x and y are interpreted +as signed integers. + +Result Type and the type of x and y must both be integer scalar or integer +vector types. Result Type and operand types must have the same number of +components with the same component width. Results are computed per +component. + +#### Example: + +```mlir +%2 = spirv.GL.SMin %0, %1 : i32 +%3 = spirv.GL.SMin %0, %1 : vector<3xi16> +``` +""" +function GL_SMin( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.SMin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_SSign` + +Result is 1 if x > 0, 0 if x = 0, or -1 if x < 0, where x is interpreted as +a signed integer. + +Result Type and the type of x must both be integer scalar or integer vector +types. Result Type and operand types must have the same number of components +with the same component width. Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.GL.SSign %0 : i32 +%3 = spirv.GL.SSign %1 : vector<3xi16> +``` +""" +function GL_SSign( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.SSign", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Sin` + +The standard trigonometric sine of x radians. + +The operand x must be a scalar or vector whose component type is 16-bit or +32-bit floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Sin %0 : f32 +%3 = spirv.GL.Sin %1 : vector<3xf16> +``` +""" +function GL_Sin(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Sin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Sinh` + +Hyperbolic sine of x radians. + +The operand x must be a scalar or vector whose component type is 16-bit or +32-bit floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Sinh %0 : f32 +%3 = spirv.GL.Sinh %1 : vector<3xf16> +``` +""" +function GL_Sinh( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Sinh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Sqrt` + +Result is the square root of x. Result is undefined if x < 0. + +The operand x must be a scalar or vector whose component type is +floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Sqrt %0 : f32 +%3 = spirv.GL.Sqrt %1 : vector<3xf16> +``` +""" +function GL_Sqrt( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Sqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Tan` + +The standard trigonometric tangent of x radians. + +The operand x must be a scalar or vector whose component type is 16-bit or +32-bit floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Tan %0 : f32 +%3 = spirv.GL.Tan %1 : vector<3xf16> +``` +""" +function GL_Tan(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Tan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_Tanh` + +Hyperbolic tangent of x radians. + +The operand x must be a scalar or vector whose component type is 16-bit or +32-bit floating-point. + +Result Type and the type of x must be the same type. Results are computed +per component. + +#### Example: + +```mlir +%2 = spirv.GL.Tanh %0 : f32 +%3 = spirv.GL.Tanh %1 : vector<3xf16> +``` +""" +function GL_Tanh( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.Tanh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_UClamp` + +Result is min(max(x, minVal), maxVal), where x, minVal and maxVal are +interpreted as unsigned integers. The resulting value is undefined if +minVal > maxVal. + +Result Type and the type of the operands must both be integer scalar or +integer vector types. Result Type and operand types must have the same number +of components with the same component width. Results are computed per +component. + + +``` +uclamp-op ::= ssa-id `=` `spirv.GL.UClamp` ssa-use, ssa-use, ssa-use `:` + unsigned-signless-scalar-vector-type +``` +#### Example: + +```mlir +%2 = spirv.GL.UClamp %x, %min, %max : i32 +%3 = spirv.GL.UClamp %x, %min, %max : vector<3xui16> +``` +""" +function GL_UClamp(x::Value, y::Value, z::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[x, y, z] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.GL.UClamp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GL_UMax` + +Result is y if x < y; otherwise result is x, where x and y are interpreted +as unsigned integers. + +Result Type and the type of x and y must both be integer scalar or integer +vector types. Result Type and operand types must have the same number of +components with the same component width. Results are computed per +component. + +#### Example: + +```mlir +%2 = spirv.GL.UMax %0, %1 : i32 +%3 = spirv.GL.UMax %0, %1 : vector<3xi16> +``` +""" +function GL_UMax( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.UMax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GL_UMin` + +Result is y if y < x; otherwise result is x, where x and y are interpreted +as unsigned integers. + +Result Type and the type of x and y must both be integer scalar or integer +vector types. Result Type and operand types must have the same number of +components with the same component width. Results are computed per +component. + +#### Example: + +```mlir +%2 = spirv.GL.UMin %0, %1 : i32 +%3 = spirv.GL.UMin %0, %1 : vector<3xi16> +``` +""" +function GL_UMin( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GL.UMin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GenericCastToPtrExplicit` + +Result Type must be an OpTypePointer. Its Storage Class must be Storage. + +Pointer must have a type of OpTypePointer whose Type is the same as the +Type of Result Type.Pointer must point to the Generic Storage Class. If +the cast fails, the instruction result is an OpConstantNull pointer in +the Storage Storage Class. + +Storage must be one of the following literal values from Storage Class: +Workgroup, CrossWorkgroup, or Function. + + + +#### Example: + +```mlir + %1 = spirv.GenericCastToPtrExplicitOp %0 : !spirv.ptr to + !spirv.ptr +``` +""" +function GenericCastToPtrExplicit(pointer::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[pointer,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.GenericCastToPtrExplicit", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GenericCastToPtr` + +Result Type must be an OpTypePointer. Its Storage Class must be +Workgroup, CrossWorkgroup, or Function. + +Pointer must point to the Generic Storage Class. + +Result Type and Pointer must point to the same type. + + + +#### Example: + +```mlir + %1 = spirv.GenericCastToPtrOp %0 : !spirv.ptr to + !spirv.ptr +``` +""" +function GenericCastToPtr(pointer::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[pointer,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.GenericCastToPtr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GlobalVariable` + +The variable type must be an OpTypePointer. Its type operand is the type of +object in memory. + +Storage Class is the Storage Class of the memory holding the object. It +cannot be Generic. It must be the same as the Storage Class operand of +the variable types. Only those storage classes that are valid at module +scope (like Input, Output, StorageBuffer, etc.) are valid. + +Initializer is optional. If Initializer is present, it will be +the initial value of the variable’s memory content. Initializer +must be an symbol defined from a constant instruction or other +`spirv.GlobalVariable` operation in module scope. Initializer must +have the same type as the type of the defined symbol. + + + +``` +variable-op ::= `spirv.GlobalVariable` spirv-type symbol-ref-id + (`initializer(` symbol-ref-id `)`)? + (`bind(` integer-literal, integer-literal `)`)? + (`built_in(` string-literal `)`)? + attribute-dict? +``` + +where `initializer` specifies initializer and `bind` specifies the +descriptor set and binding number. `built_in` specifies SPIR-V +BuiltIn decoration associated with the op. + +#### Example: + +```mlir +spirv.GlobalVariable @var0 : !spirv.ptr @var0 +spirv.GlobalVariable @var1 initializer(@var0) : !spirv.ptr +spirv.GlobalVariable @var2 bind(1, 2) : !spirv.ptr +spirv.GlobalVariable @var3 built_in(\"GlobalInvocationId\") : !spirv.ptr, Input> +``` +""" +function GlobalVariable(; + type, + sym_name, + initializer=nothing, + location_=nothing, + binding=nothing, + descriptor_set=nothing, + builtin=nothing, + linkage_attributes=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("type", type), namedattribute("sym_name", sym_name) + ] + !isnothing(initializer) && + push!(_attributes, namedattribute("initializer", initializer)) + !isnothing(location) && push!(_attributes, namedattribute("location", location_)) + !isnothing(binding) && push!(_attributes, namedattribute("binding", binding)) + !isnothing(descriptor_set) && + push!(_attributes, namedattribute("descriptor_set", descriptor_set)) + !isnothing(builtin) && push!(_attributes, namedattribute("builtin", builtin)) + !isnothing(linkage_attributes) && + push!(_attributes, namedattribute("linkage_attributes", linkage_attributes)) + + return IR.create_operation( + "spirv.GlobalVariable", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupBroadcast` + +All invocations of this module within Execution must reach this point of +execution. + +Behavior is undefined if this instruction is used in control flow that +is non-uniform within Execution. + +Result Type must be a scalar or vector of floating-point type, integer +type, or Boolean type. + +Execution must be Workgroup or Subgroup Scope. + +The type of Value must be the same as Result Type. + +LocalId must be an integer datatype. It can be a scalar, or a vector +with 2 components or a vector with 3 components. LocalId must be the +same for all invocations in the group. + +#### Example: + +```mlir +%scalar_value = ... : f32 +%vector_value = ... : vector<4xf32> +%scalar_localid = ... : i32 +%vector_localid = ... : vector<3xi32> +%0 = spirv.GroupBroadcast \"Subgroup\" %scalar_value, %scalar_localid : f32, i32 +%1 = spirv.GroupBroadcast \"Workgroup\" %vector_value, %vector_localid : + vector<4xf32>, vector<3xi32> +``` +""" +function GroupBroadcast( + value::Value, + localid::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, localid] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("execution_scope", execution_scope),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupBroadcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupFAdd` + +Behavior is undefined if not all invocations of this module within +Execution reach this point of execution. + +Behavior is undefined unless all invocations within Execution execute +the same dynamic instance of this instruction. + +Result Type must be a scalar or vector of floating-point type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is 0. + +The type of X must be the same as Result Type. + +#### Example: + +```mlir +%0 = spirv.GroupFAdd %value : f32 +``` +""" +function GroupFAdd( + x::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupFAdd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupFMax` + +Behavior is undefined if not all invocations of this module within +Execution reach this point of execution. + +Behavior is undefined unless all invocations within Execution execute +the same dynamic instance of this instruction. + +Result Type must be a scalar or vector of floating-point type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is -INF. + +The type of X must be the same as Result Type. + +#### Example: + +```mlir +%0 = spirv.GroupFMax %value : f32 +``` +""" +function GroupFMax( + x::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupFMax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupFMin` + +Behavior is undefined if not all invocations of this module within +Execution reach this point of execution. + +Behavior is undefined unless all invocations within Execution execute +the same dynamic instance of this instruction. + +Result Type must be a scalar or vector of floating-point type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is +INF. + +The type of X must be the same as Result Type. + +#### Example: + +```mlir +%0 = spirv.GroupFMin %value : f32 +``` +""" +function GroupFMin( + x::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupFMin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`KHR_GroupFMul` + +Behavior is undefined if not all invocations of this module within +\'Execution\' reach this point of execution. + +Behavior is undefined unless all invocations within \'Execution\' execute the +same dynamic instance of this instruction. + +\'Result Type\' must be a scalar or vector of floating-point type. + +\'Execution\' is a Scope. It must be either Workgroup or Subgroup. + +The identity I for \'Operation\' is 1. + +The type of \'X\' must be the same as \'Result Type\'. + + +#### Example: + +```mlir +%0 = spirv.KHR.GroupFMul %value : f32 +``` +""" +function KHR_GroupFMul( + x::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.KHR.GroupFMul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupIAdd` + +Behavior is undefined if not all invocations of this module within +Execution reach this point of execution. + +Behavior is undefined unless all invocations within Execution execute +the same dynamic instance of this instruction. + +Result Type must be a scalar or vector of integer type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is 0. + +The type of X must be the same as Result Type. + +#### Example: + +```mlir +%0 = spirv.GroupIAdd %value : i32 +``` +""" +function GroupIAdd( + x::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupIAdd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`KHR_GroupIMul` + +Behavior is undefined if not all invocations of this module within +\'Execution\' reach this point of execution. + +Behavior is undefined unless all invocations within \'Execution\' execute the +same dynamic instance of this instruction. + +\'Result Type\' must be a scalar or vector of integer type. + +\'Execution\' is a Scope. It must be either Workgroup or Subgroup. + +The identity I for \'Operation\' is 1. + +The type of \'X\' must be the same as \'Result Type\'. + +#### Example: + +```mlir +%0 = spirv.KHR.GroupIMul %value : i32 +``` +""" +function KHR_GroupIMul( + x::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.KHR.GroupIMul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupNonUniformBallot` + +Result Type must be a vector of four components of integer type scalar, +whose Signedness operand is 0. + +Result is a set of bitfields where the first invocation is represented +in the lowest bit of the first vector component and the last (up to the +size of the group) is the higher bit number of the last bitmask needed +to represent all bits of the group invocations. + +Execution must be Workgroup or Subgroup Scope. + +Predicate must be a Boolean type. + +#### Example: + +```mlir +%0 = spirv.GroupNonUniformBallot \"SubGroup\" %predicate : vector<4xi32> +``` +""" +function GroupNonUniformBallot( + predicate::Value; result::IR.Type, execution_scope, location=Location() +) + _results = IR.Type[result,] + _operands = Value[predicate,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("execution_scope", execution_scope),] + + return IR.create_operation( + "spirv.GroupNonUniformBallot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformBitwiseAnd` + +Result Type must be a scalar or vector of integer type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is ~0. If Operation is ClusteredReduce, +ClusterSize must be present. + +The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i32 +%vector = ... : vector<4xi32> +%0 = spirv.GroupNonUniformBitwiseAnd \"Workgroup\" \"Reduce\" %scalar : i32 +%1 = spirv.GroupNonUniformBitwiseAnd \"Subgroup\" \"ClusteredReduce\" + %vector cluster_size(%four) : vector<4xi32> +``` +""" +function GroupNonUniformBitwiseAnd( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformBitwiseAnd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformBitwiseOr` + +Result Type must be a scalar or vector of integer type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is 0. If Operation is ClusteredReduce, +ClusterSize must be present. + +The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i32 +%vector = ... : vector<4xi32> +%0 = spirv.GroupNonUniformBitwiseOr \"Workgroup\" \"Reduce\" %scalar : i32 +%1 = spirv.GroupNonUniformBitwiseOr \"Subgroup\" \"ClusteredReduce\" + %vector cluster_size(%four) : vector<4xi32> +``` +""" +function GroupNonUniformBitwiseOr( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformBitwiseOr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformBitwiseXor` + +Result Type must be a scalar or vector of integer type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is 0. If Operation is ClusteredReduce, +ClusterSize must be present. + +The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i32 +%vector = ... : vector<4xi32> +%0 = spirv.GroupNonUniformBitwiseXor \"Workgroup\" \"Reduce\" %scalar : i32 +%1 = spirv.GroupNonUniformBitwiseXor \"Subgroup\" \"ClusteredReduce\" + %vector cluster_size(%four) : vector<4xi32> +``` +""" +function GroupNonUniformBitwiseXor( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformBitwiseXor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformBroadcast` + +Result Type must be a scalar or vector of floating-point type, integer +type, or Boolean type. + +Execution must be Workgroup or Subgroup Scope. + +The type of Value must be the same as Result Type. + +Id must be a scalar of integer type, whose Signedness operand is 0. + +Before version 1.5, Id must come from a constant instruction. Starting +with version 1.5, Id must be dynamically uniform. + +The resulting value is undefined if Id is an inactive invocation, or is +greater than or equal to the size of the group. + +#### Example: + +```mlir +%scalar_value = ... : f32 +%vector_value = ... : vector<4xf32> +%id = ... : i32 +%0 = spirv.GroupNonUniformBroadcast \"Subgroup\" %scalar_value, %id : f32, i32 +%1 = spirv.GroupNonUniformBroadcast \"Workgroup\" %vector_value, %id : + vector<4xf32>, i32 +``` +""" +function GroupNonUniformBroadcast( + value::Value, + id::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, id] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("execution_scope", execution_scope),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupNonUniformBroadcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupNonUniformElect` + +Result Type must be a Boolean type. + +Execution must be Workgroup or Subgroup Scope. + +#### Example: + +```mlir +%0 = spirv.GroupNonUniformElect : i1 +``` +""" +function GroupNonUniformElect(; + result=nothing::Union{Nothing,IR.Type}, execution_scope, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("execution_scope", execution_scope),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupNonUniformElect", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupNonUniformFAdd` + +Result Type must be a scalar or vector of floating-point type. + +Execution must be Workgroup or Subgroup Scope. + +The identity I for Operation is 0. If Operation is ClusteredReduce, +ClusterSize must be specified. + + The type of Value must be the same as Result Type. The method used to +perform the group operation on the contributed Value(s) from active +invocations is implementation defined. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +``` +scope ::= `\"Workgroup\"` | `\"Subgroup\"` +operation ::= `\"Reduce\"` | `\"InclusiveScan\"` | `\"ExclusiveScan\"` | ... +float-scalar-vector-type ::= float-type | + `vector<` integer-literal `x` float-type `>` +non-uniform-fadd-op ::= ssa-id `=` `spirv.GroupNonUniformFAdd` scope operation + ssa-use ( `cluster_size` `(` ssa_use `)` )? + `:` float-scalar-vector-type +``` + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : f32 +%vector = ... : vector<4xf32> +%0 = spirv.GroupNonUniformFAdd \"Workgroup\" \"Reduce\" %scalar : f32 +%1 = spirv.GroupNonUniformFAdd \"Subgroup\" \"ClusteredReduce\" %vector cluster_size(%four) : vector<4xf32> +``` +""" +function GroupNonUniformFAdd( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformFAdd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformFMax` + +Result Type must be a scalar or vector of floating-point type. + +Execution must be Workgroup or Subgroup Scope. + +The identity I for Operation is -INF. If Operation is ClusteredReduce, +ClusterSize must be specified. + + The type of Value must be the same as Result Type. The method used to +perform the group operation on the contributed Value(s) from active +invocations is implementation defined. From the set of Value(s) provided +by active invocations within a subgroup, if for any two Values one of +them is a NaN, the other is chosen. If all Value(s) that are used by the +current invocation are NaN, then the result is an undefined value. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +``` +scope ::= `\"Workgroup\"` | `\"Subgroup\"` +operation ::= `\"Reduce\"` | `\"InclusiveScan\"` | `\"ExclusiveScan\"` | ... +float-scalar-vector-type ::= float-type | + `vector<` integer-literal `x` float-type `>` +non-uniform-fmax-op ::= ssa-id `=` `spirv.GroupNonUniformFMax` scope operation + ssa-use ( `cluster_size` `(` ssa_use `)` )? + `:` float-scalar-vector-type +``` + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : f32 +%vector = ... : vector<4xf32> +%0 = spirv.GroupNonUniformFMax \"Workgroup\" \"Reduce\" %scalar : f32 +%1 = spirv.GroupNonUniformFMax \"Subgroup\" \"ClusteredReduce\" %vector cluster_size(%four) : vector<4xf32> +``` +""" +function GroupNonUniformFMax( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformFMax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformFMin` + +Result Type must be a scalar or vector of floating-point type. + +Execution must be Workgroup or Subgroup Scope. + +The identity I for Operation is +INF. If Operation is ClusteredReduce, +ClusterSize must be specified. + + The type of Value must be the same as Result Type. The method used to +perform the group operation on the contributed Value(s) from active +invocations is implementation defined. From the set of Value(s) provided +by active invocations within a subgroup, if for any two Values one of +them is a NaN, the other is chosen. If all Value(s) that are used by the +current invocation are NaN, then the result is an undefined value. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +``` +scope ::= `\"Workgroup\"` | `\"Subgroup\"` +operation ::= `\"Reduce\"` | `\"InclusiveScan\"` | `\"ExclusiveScan\"` | ... +float-scalar-vector-type ::= float-type | + `vector<` integer-literal `x` float-type `>` +non-uniform-fmin-op ::= ssa-id `=` `spirv.GroupNonUniformFMin` scope operation + ssa-use ( `cluster_size` `(` ssa_use `)` )? + `:` float-scalar-vector-type +``` + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : f32 +%vector = ... : vector<4xf32> +%0 = spirv.GroupNonUniformFMin \"Workgroup\" \"Reduce\" %scalar : f32 +%1 = spirv.GroupNonUniformFMin \"Subgroup\" \"ClusteredReduce\" %vector cluster_size(%four) : vector<4xf32> +``` +""" +function GroupNonUniformFMin( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformFMin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformFMul` + +Result Type must be a scalar or vector of floating-point type. + +Execution must be Workgroup or Subgroup Scope. + +The identity I for Operation is 1. If Operation is ClusteredReduce, +ClusterSize must be specified. + + The type of Value must be the same as Result Type. The method used to +perform the group operation on the contributed Value(s) from active +invocations is implementation defined. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +``` +scope ::= `\"Workgroup\"` | `\"Subgroup\"` +operation ::= `\"Reduce\"` | `\"InclusiveScan\"` | `\"ExclusiveScan\"` | ... +float-scalar-vector-type ::= float-type | + `vector<` integer-literal `x` float-type `>` +non-uniform-fmul-op ::= ssa-id `=` `spirv.GroupNonUniformFMul` scope operation + ssa-use ( `cluster_size` `(` ssa_use `)` )? + `:` float-scalar-vector-type +``` + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : f32 +%vector = ... : vector<4xf32> +%0 = spirv.GroupNonUniformFMul \"Workgroup\" \"Reduce\" %scalar : f32 +%1 = spirv.GroupNonUniformFMul \"Subgroup\" \"ClusteredReduce\" %vector cluster_size(%four) : vector<4xf32> +``` +""" +function GroupNonUniformFMul( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformFMul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformIAdd` + +Result Type must be a scalar or vector of integer type. + +Execution must be Workgroup or Subgroup Scope. + +The identity I for Operation is 0. If Operation is ClusteredReduce, +ClusterSize must be specified. + + The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +``` +scope ::= `\"Workgroup\"` | `\"Subgroup\"` +operation ::= `\"Reduce\"` | `\"InclusiveScan\"` | `\"ExclusiveScan\"` | ... +integer-scalar-vector-type ::= integer-type | + `vector<` integer-literal `x` integer-type `>` +non-uniform-iadd-op ::= ssa-id `=` `spirv.GroupNonUniformIAdd` scope operation + ssa-use ( `cluster_size` `(` ssa_use `)` )? + `:` integer-scalar-vector-type +``` + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i32 +%vector = ... : vector<4xi32> +%0 = spirv.GroupNonUniformIAdd \"Workgroup\" \"Reduce\" %scalar : i32 +%1 = spirv.GroupNonUniformIAdd \"Subgroup\" \"ClusteredReduce\" %vector cluster_size(%four) : vector<4xi32> +``` +""" +function GroupNonUniformIAdd( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformIAdd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformIMul` + +Result Type must be a scalar or vector of integer type. + +Execution must be Workgroup or Subgroup Scope. + +The identity I for Operation is 1. If Operation is ClusteredReduce, +ClusterSize must be specified. + + The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +``` +scope ::= `\"Workgroup\"` | `\"Subgroup\"` +operation ::= `\"Reduce\"` | `\"InclusiveScan\"` | `\"ExclusiveScan\"` | ... +integer-scalar-vector-type ::= integer-type | + `vector<` integer-literal `x` integer-type `>` +non-uniform-imul-op ::= ssa-id `=` `spirv.GroupNonUniformIMul` scope operation + ssa-use ( `cluster_size` `(` ssa_use `)` )? + `:` integer-scalar-vector-type +``` + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i32 +%vector = ... : vector<4xi32> +%0 = spirv.GroupNonUniformIMul \"Workgroup\" \"Reduce\" %scalar : i32 +%1 = spirv.GroupNonUniformIMul \"Subgroup\" \"ClusteredReduce\" %vector cluster_size(%four) : vector<4xi32> +``` +""" +function GroupNonUniformIMul( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformIMul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformLogicalAnd` + +Result Type must be a scalar or vector of Boolean type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is ~0. If Operation is ClusteredReduce, +ClusterSize must be present. + +The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i1 +%vector = ... : vector<4xi1> +%0 = spirv.GroupNonUniformLogicalAnd \"Workgroup\" \"Reduce\" %scalar : i1 +%1 = spirv.GroupNonUniformLogicalAnd \"Subgroup\" \"ClusteredReduce\" + %vector cluster_size(%four) : vector<4xi1> +``` +""" +function GroupNonUniformLogicalAnd( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformLogicalAnd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformLogicalOr` + +Result Type must be a scalar or vector of Boolean type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is 0. If Operation is ClusteredReduce, +ClusterSize must be present. + +The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i1 +%vector = ... : vector<4xi1> +%0 = spirv.GroupNonUniformLogicalOr \"Workgroup\" \"Reduce\" %scalar : i1 +%1 = spirv.GroupNonUniformLogicalOr \"Subgroup\" \"ClusteredReduce\" + %vector cluster_size(%four) : vector<4xi1> +``` +""" +function GroupNonUniformLogicalOr( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformLogicalOr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformLogicalXor` + +Result Type must be a scalar or vector of Boolean type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is 0. If Operation is ClusteredReduce, +ClusterSize must be present. + +The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i1 +%vector = ... : vector<4xi1> +%0 = spirv.GroupNonUniformLogicalXor \"Workgroup\" \"Reduce\" %scalar : i1 +%1 = spirv.GroupNonUniformLogicalXor \"Subgroup\" \"ClusteredReduce\" + %vector cluster_size(%four) : vector<4xi> +``` +""" +function GroupNonUniformLogicalXor( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformLogicalXor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformSMax` + +Result Type must be a scalar or vector of integer type. + +Execution must be Workgroup or Subgroup Scope. + +The identity I for Operation is INT_MIN. If Operation is +ClusteredReduce, ClusterSize must be specified. + + The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +``` +scope ::= `\"Workgroup\"` | `\"Subgroup\"` +operation ::= `\"Reduce\"` | `\"InclusiveScan\"` | `\"ExclusiveScan\"` | ... +integer-scalar-vector-type ::= integer-type | + `vector<` integer-literal `x` integer-type `>` +non-uniform-smax-op ::= ssa-id `=` `spirv.GroupNonUniformSMax` scope operation + ssa-use ( `cluster_size` `(` ssa_use `)` )? + `:` integer-scalar-vector-type +``` + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i32 +%vector = ... : vector<4xi32> +%0 = spirv.GroupNonUniformSMax \"Workgroup\" \"Reduce\" %scalar : i32 +%1 = spirv.GroupNonUniformSMax \"Subgroup\" \"ClusteredReduce\" %vector cluster_size(%four) : vector<4xi32> +``` +""" +function GroupNonUniformSMax( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformSMax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformSMin` + +Result Type must be a scalar or vector of integer type. + +Execution must be Workgroup or Subgroup Scope. + +The identity I for Operation is INT_MAX. If Operation is +ClusteredReduce, ClusterSize must be specified. + + The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +``` +scope ::= `\"Workgroup\"` | `\"Subgroup\"` +operation ::= `\"Reduce\"` | `\"InclusiveScan\"` | `\"ExclusiveScan\"` | ... +integer-scalar-vector-type ::= integer-type | + `vector<` integer-literal `x` integer-type `>` +non-uniform-smin-op ::= ssa-id `=` `spirv.GroupNonUniformSMin` scope operation + ssa-use ( `cluster_size` `(` ssa_use `)` )? + `:` integer-scalar-vector-type +``` + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i32 +%vector = ... : vector<4xi32> +%0 = spirv.GroupNonUniformSMin \"Workgroup\" \"Reduce\" %scalar : i32 +%1 = spirv.GroupNonUniformSMin \"Subgroup\" \"ClusteredReduce\" %vector cluster_size(%four) : vector<4xi32> +``` +""" +function GroupNonUniformSMin( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformSMin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformShuffleDown` + +Result Type must be a scalar or vector of floating-point type, integer +type, or Boolean type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + + The type of Value must be the same as Result Type. + +Delta must be a scalar of integer type, whose Signedness operand is 0. + +Delta is treated as unsigned and the resulting value is undefined if +Delta is greater than or equal to the size of the group, or if the +current invocation’s id within the group + Delta is either an inactive +invocation or greater than or equal to the size of the group. + + + +#### Example: + +```mlir +%0 = spirv.GroupNonUniformShuffleDown %val, %delta : f32, i32 +``` +""" +function GroupNonUniformShuffleDown( + value::Value, + delta::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, delta] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("execution_scope", execution_scope),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupNonUniformShuffleDown", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupNonUniformShuffle` + +Result Type must be a scalar or vector of floating-point type, integer +type, or Boolean type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + + The type of Value must be the same as Result Type. + +Id must be a scalar of integer type, whose Signedness operand is 0. + +The resulting value is undefined if Id is an inactive invocation, or is +greater than or equal to the size of the group. + + + +#### Example: + +```mlir +%0 = spirv.GroupNonUniformShuffle %val, %id : f32, i32 +``` +""" +function GroupNonUniformShuffle( + value::Value, + id::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, id] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("execution_scope", execution_scope),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupNonUniformShuffle", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupNonUniformShuffleUp` + +Result Type must be a scalar or vector of floating-point type, integer +type, or Boolean type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + + The type of Value must be the same as Result Type. + +Delta must be a scalar of integer type, whose Signedness operand is 0. + +Delta is treated as unsigned and the resulting value is undefined if +Delta is greater than the current invocation’s id within the group or if +the selected lane is inactive. + + + +#### Example: + +```mlir +%0 = spirv.GroupNonUniformShuffleUp %val, %delta : f32, i32 +``` +""" +function GroupNonUniformShuffleUp( + value::Value, + delta::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, delta] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("execution_scope", execution_scope),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupNonUniformShuffleUp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupNonUniformShuffleXor` + +Result Type must be a scalar or vector of floating-point type, integer +type, or Boolean type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + + The type of Value must be the same as Result Type. + +Mask must be a scalar of integer type, whose Signedness operand is 0. + +The resulting value is undefined if current invocation’s id within the +group xor’ed with Mask is an inactive invocation, or is greater than or +equal to the size of the group. + + + +#### Example: + +```mlir +%0 = spirv.GroupNonUniformShuffleXor %val, %mask : f32, i32 +``` +""" +function GroupNonUniformShuffleXor( + value::Value, + mask::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, mask] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("execution_scope", execution_scope),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupNonUniformShuffleXor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupNonUniformUMax` + +Result Type must be a scalar or vector of integer type, whose +Signedness operand is 0. + +Execution must be Workgroup or Subgroup Scope. + +The identity I for Operation is 0. If Operation is ClusteredReduce, +ClusterSize must be specified. + + The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +``` +scope ::= `\"Workgroup\"` | `\"Subgroup\"` +operation ::= `\"Reduce\"` | `\"InclusiveScan\"` | `\"ExclusiveScan\"` | ... +integer-scalar-vector-type ::= integer-type | + `vector<` integer-literal `x` integer-type `>` +non-uniform-umax-op ::= ssa-id `=` `spirv.GroupNonUniformUMax` scope operation + ssa-use ( `cluster_size` `(` ssa_use `)` )? + `:` integer-scalar-vector-type +``` + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i32 +%vector = ... : vector<4xi32> +%0 = spirv.GroupNonUniformUMax \"Workgroup\" \"Reduce\" %scalar : i32 +%1 = spirv.GroupNonUniformUMax \"Subgroup\" \"ClusteredReduce\" %vector cluster_size(%four) : vector<4xi32> +``` +""" +function GroupNonUniformUMax( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformUMax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupNonUniformUMin` + +Result Type must be a scalar or vector of integer type, whose +Signedness operand is 0. + +Execution must be Workgroup or Subgroup Scope. + +The identity I for Operation is UINT_MAX. If Operation is +ClusteredReduce, ClusterSize must be specified. + + The type of Value must be the same as Result Type. + +ClusterSize is the size of cluster to use. ClusterSize must be a scalar +of integer type, whose Signedness operand is 0. ClusterSize must come +from a constant instruction. ClusterSize must be at least 1, and must be +a power of 2. If ClusterSize is greater than the declared SubGroupSize, +executing this instruction results in undefined behavior. + + + +``` +scope ::= `\"Workgroup\"` | `\"Subgroup\"` +operation ::= `\"Reduce\"` | `\"InclusiveScan\"` | `\"ExclusiveScan\"` | ... +integer-scalar-vector-type ::= integer-type | + `vector<` integer-literal `x` integer-type `>` +non-uniform-umin-op ::= ssa-id `=` `spirv.GroupNonUniformUMin` scope operation + ssa-use ( `cluster_size` `(` ssa_use `)` )? + `:` integer-scalar-vector-type +``` + +#### Example: + +```mlir +%four = spirv.Constant 4 : i32 +%scalar = ... : i32 +%vector = ... : vector<4xi32> +%0 = spirv.GroupNonUniformUMin \"Workgroup\" \"Reduce\" %scalar : i32 +%1 = spirv.GroupNonUniformUMin \"Subgroup\" \"ClusteredReduce\" %vector cluster_size(%four) : vector<4xi32> +``` +""" +function GroupNonUniformUMin( + value::Value, + cluster_size=nothing::Union{Nothing,Value}; + result::IR.Type, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(cluster_size) && push!(_operands, cluster_size) + + return IR.create_operation( + "spirv.GroupNonUniformUMin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`GroupSMax` + +Behavior is undefined if not all invocations of this module within +Execution reach this point of execution. + +Behavior is undefined unless all invocations within Execution execute +the same dynamic instance of this instruction. + +Result Type must be a scalar or vector of integer type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is INT_MIN when X is 32 bits wide and +LONG_MIN when X is 64 bits wide. + +The type of X must be the same as Result Type. + +#### Example: + +```mlir +%0 = spirv.GroupSMax %value : i32 +``` +""" +function GroupSMax( + x::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupSMax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupSMin` + +Behavior is undefined if not all invocations of this module within +Execution reach this point of execution. + +Behavior is undefined unless all invocations within Execution execute +the same dynamic instance of this instruction. + +Result Type must be a scalar or vector of integer type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is INT_MAX when X is 32 bits wide and +LONG_MAX when X is 64 bits wide. + +The type of X must be the same as Result Type. + +#### Example: + +```mlir +%0 = spirv.GroupSMin %value : i32 +``` +""" +function GroupSMin( + x::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupSMin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupUMax` + +Behavior is undefined if not all invocations of this module within +Execution reach this point of execution. + +Behavior is undefined unless all invocations within Execution execute +the same dynamic instance of this instruction. + +Result Type must be a scalar or vector of integer type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is 0. + +The type of X must be the same as Result Type. + +#### Example: + +```mlir +%0 = spirv.GroupUMax %value : i32 +``` +""" +function GroupUMax( + x::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupUMax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`GroupUMin` + +Behavior is undefined if not all invocations of this module within +Execution reach this point of execution. + +Behavior is undefined unless all invocations within Execution execute +the same dynamic instance of this instruction. + +Result Type must be a scalar or vector of integer type. + +Execution is a Scope. It must be either Workgroup or Subgroup. + +The identity I for Operation is UINT_MAX when X is 32 bits wide and +ULONG_MAX when X is 64 bits wide. + +The type of X must be the same as Result Type. + +#### Example: + +```mlir +%0 = spirv.GroupUMin %value : i32 +``` +""" +function GroupUMin( + x::Value; + result=nothing::Union{Nothing,IR.Type}, + execution_scope, + group_operation, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("execution_scope", execution_scope), + namedattribute("group_operation", group_operation), + ] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.GroupUMin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`IAddCarry` + +Result Type must be from OpTypeStruct. The struct must have two +members, and the two members must be the same type. The member type +must be a scalar or vector of integer type, whose Signedness operand is +0. + +Operand 1 and Operand 2 must have the same type as the members of Result +Type. These are consumed as unsigned integers. + + Results are computed per component. + +Member 0 of the result gets the low-order bits (full component width) of +the addition. + +Member 1 of the result gets the high-order (carry) bit of the result of +the addition. That is, it gets the value 1 if the addition overflowed +the component width, and 0 otherwise. + + + +#### Example: + +```mlir +%2 = spirv.IAddCarry %0, %1 : !spirv.struct<(i32, i32)> +%2 = spirv.IAddCarry %0, %1 : !spirv.struct<(vector<2xi32>, vector<2xi32>)> +``` +""" +function IAddCarry(operand1::Value, operand2::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.IAddCarry", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`IAdd` + +Result Type must be a scalar or vector of integer type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same number of components as Result +Type. They must have the same component width as Result Type. + +The resulting value will equal the low-order N bits of the correct +result R, where N is the component width and R is computed with enough +precision to avoid overflow and underflow. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.IAdd %0, %1 : i32 +%5 = spirv.IAdd %2, %3 : vector<4xi32> + +``` +""" +function IAdd( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.IAdd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`IEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same component width, and they must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.IEqual %0, %1 : i32 +%5 = spirv.IEqual %2, %3 : vector<4xi32> +``` +""" +function IEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.IEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`IMul` + +Result Type must be a scalar or vector of integer type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same number of components as Result +Type. They must have the same component width as Result Type. + +The resulting value will equal the low-order N bits of the correct +result R, where N is the component width and R is computed with enough +precision to avoid overflow and underflow. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.IMul %0, %1 : i32 +%5 = spirv.IMul %2, %3 : vector<4xi32> + +``` +""" +function IMul( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.IMul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`INTEL_ConvertBF16ToF` + +Interpret a 16-bit integer as bfloat16 and convert the value numerically to 32-bit floating point type. + +Result Type must be a scalar or vector of floating-point. The component width must be 32 bits. + +Bfloat16 Value must be a scalar or vector of integer type, which is interpreted as a bfloat16 type. +The type must have the same number of components as the Result Type. The component width must be 16 bits. + +Results are computed per component. + +#### Example: + +```mlir +%1 = spirv.ConvertBF16ToF %0 : i16 to f32 +%3 = spirv.ConvertBF16ToF %2 : vector<3xi16> to vector<3xf32> +``` +""" +function INTEL_ConvertBF16ToF(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.INTEL.ConvertBF16ToF", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`INTEL_ConvertFToBF16` + +Convert value numerically from 32-bit floating point to bfloat16, +which is represented as a 16-bit unsigned integer. + +Result Type must be a scalar or vector of integer type. +The component width must be 16 bits. Bit pattern in the Result represents a bfloat16 value. + +Float Value must be a scalar or vector of floating-point type. +It must have the same number of components as Result Type. The component width must be 32 bits. + +Results are computed per component. + +#### Example: + +```mlir +%1 = spirv.ConvertFToBF16 %0 : f32 to i16 +%3 = spirv.ConvertFToBF16 %2 : vector<3xf32> to vector<3xi16> +``` +""" +function INTEL_ConvertFToBF16(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.INTEL.ConvertFToBF16", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`INTEL_JointMatrixLoad` + +Load a matrix through a pointer. + +Result Type is the type of the loaded matrix. It must be OpTypeJointMatrixINTEL. + +Pointer is the pointer to load through. It specifies start of memory region where +elements of the matrix are stored and arranged according to Layout. + +Stride is the number of elements in memory between beginnings of successive rows, +columns (or words) in the result. It must be a scalar integer type. + +Layout indicates how the values loaded from memory are arranged. It must be the +result of a constant instruction. + +Scope is syncronization scope for operation on the matrix. It must be the result +of a constant instruction with scalar integer type. + +If present, any Memory Operands must begin with a memory operand literal. If not +present, it is the same as specifying the memory operand None. + +#### Example: +```mlir +%0 = spirv.INTEL.JointMatrixLoad %ptr, %stride + {memory_access = #spirv.memory_access} : + (!spirv.ptr, i32) -> + !spirv.jointmatrix<8x16xi32, ColumnMajor, Subgroup> +``` +""" +function INTEL_JointMatrixLoad( + pointer::Value, + stride::Value; + result::IR.Type, + layout, + scope, + memory_access=nothing, + alignment=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[pointer, stride] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("layout", layout), namedattribute("scope", scope) + ] + !isnothing(memory_access) && + push!(_attributes, namedattribute("memory_access", memory_access)) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + + return IR.create_operation( + "spirv.INTEL.JointMatrixLoad", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`INTEL_JointMatrixMad` + +Multiply matrix A by matrix B and add matrix C to the result +of the multiplication: A*B+C. Here A is a M x K matrix, B is +a K x N matrix and C is a M x N matrix. + +Behavior is undefined if sizes of operands do not meet the +conditions above. All operands and the Result Type must be +OpTypeJointMatrixINTEL. + +A must be a OpTypeJointMatrixINTEL whose Component Type is a +signed numerical type, Row Count equals to M and Column Count +equals to K + +B must be a OpTypeJointMatrixINTEL whose Component Type is a +signed numerical type, Row Count equals to K and Column Count +equals to N + +C and Result Type must be a OpTypeJointMatrixINTEL with Row +Count equals to M and Column Count equals to N + +Scope is syncronization scope for operation on the matrix. +It must be the result of a constant instruction with scalar +integer type. + +#### Example: +```mlir +%r = spirv.INTEL.JointMatrixMad %a, %b, %c : + !spirv.jointmatrix<8x32xi8, RowMajor, Subgroup>, + !spirv.jointmatrix<32x8xi8, ColumnMajor, Subgroup> + -> !spirv.jointmatrix<8x8xi32, RowMajor, Subgroup> +``` +""" +function INTEL_JointMatrixMad( + a::Value, + b::Value, + c::Value; + result=nothing::Union{Nothing,IR.Type}, + scope, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b, c] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("scope", scope),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.INTEL.JointMatrixMad", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`INTEL_JointMatrixStore` + +Store a matrix through a pointer. + +Pointer is the pointer to store through. It specifies +start of memory region where elements of the matrix must +be stored and arranged according to Layout. + +Object is the matrix to store. It must be +OpTypeJointMatrixINTEL. + +Stride is the number of elements in memory between beginnings +of successive rows, columns (or words) of the Object. It must +be a scalar integer type. + +Layout indicates how the values stored to memory are arranged. +It must be the result of a constant instruction. + +Scope is syncronization scope for operation on the matrix. +It must be the result of a constant instruction with scalar +integer type. + +If present, any Memory Operands must begin with a memory operand +literal. If not present, it is the same as specifying the memory +operand None. + +#### Example: +```mlir +spirv.INTEL.JointMatrixStore %ptr, %m, %stride +{memory_access = #spirv.memory_access} : (!spirv.ptr, +!spirv.jointmatrix<8x16xi32, RowMajor, Subgroup>, i32) +``` +""" +function INTEL_JointMatrixStore( + pointer::Value, + object::Value, + stride::Value; + layout, + scope, + memory_access=nothing, + alignment=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, object, stride] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("layout", layout), namedattribute("scope", scope) + ] + !isnothing(memory_access) && + push!(_attributes, namedattribute("memory_access", memory_access)) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + + return IR.create_operation( + "spirv.INTEL.JointMatrixStore", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`INTEL_JointMatrixWorkItemLength` + +Return number of components owned by the current work-item in +a joint matrix. + +Result Type must be an 32-bit unsigned integer type scalar. + +Type is a joint matrix type. + +#### Example: + +``` +%0 = spirv.INTEL.JointMatrixWorkItemLength : !spirv.jointmatrix +``` +""" +function INTEL_JointMatrixWorkItemLength(; + result=nothing::Union{Nothing,IR.Type}, joint_matrix_type, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("joint_matrix_type", joint_matrix_type),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.INTEL.JointMatrixWorkItemLength", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`INTEL_SubgroupBlockRead` + +Reads one or more components of Result data for each invocation in the +subgroup from the specified Ptr as a block operation. + +The data is read strided, so the first value read is: +Ptr[ SubgroupLocalInvocationId ] + +and the second value read is: +Ptr[ SubgroupLocalInvocationId + SubgroupMaxSize ] +etc. + +Result Type may be a scalar or vector type, and its component type must be +equal to the type pointed to by Ptr. + +The type of Ptr must be a pointer type, and must point to a scalar type. + + + +``` +subgroup-block-read-INTEL-op ::= ssa-id `=` `spirv.INTEL.SubgroupBlockRead` + storage-class ssa_use `:` spirv-element-type +``` + +#### Example: + +```mlir +%0 = spirv.INTEL.SubgroupBlockRead \"StorageBuffer\" %ptr : i32 +``` +""" +function INTEL_SubgroupBlockRead(ptr::Value; value::IR.Type, location=Location()) + _results = IR.Type[value,] + _operands = Value[ptr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.INTEL.SubgroupBlockRead", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`INTEL_SubgroupBlockWrite` + +Writes one or more components of Data for each invocation in the subgroup +from the specified Ptr as a block operation. + +The data is written strided, so the first value is written to: +Ptr[ SubgroupLocalInvocationId ] + +and the second value written is: +Ptr[ SubgroupLocalInvocationId + SubgroupMaxSize ] +etc. + +The type of Ptr must be a pointer type, and must point to a scalar type. + +The component type of Data must be equal to the type pointed to by Ptr. + + + +``` +subgroup-block-write-INTEL-op ::= ssa-id `=` `spirv.INTEL.SubgroupBlockWrite` + storage-class ssa_use `,` ssa-use `:` spirv-element-type +``` + +#### Example: + +```mlir +spirv.INTEL.SubgroupBlockWrite \"StorageBuffer\" %ptr, %value : i32 +``` +""" +function INTEL_SubgroupBlockWrite(ptr::Value, value::Value; location=Location()) + _results = IR.Type[] + _operands = Value[ptr, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.INTEL.SubgroupBlockWrite", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`INotEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same component width, and they must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.INotEqual %0, %1 : i32 +%5 = spirv.INotEqual %2, %3 : vector<4xi32> + +``` +""" +function INotEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.INotEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ISubBorrow` + +Result Type must be from OpTypeStruct. The struct must have two +members, and the two members must be the same type. The member type +must be a scalar or vector of integer type, whose Signedness operand is +0. + +Operand 1 and Operand 2 must have the same type as the members of Result +Type. These are consumed as unsigned integers. + + Results are computed per component. + +Member 0 of the result gets the low-order bits (full component width) of +the subtraction. That is, if Operand 1 is larger than Operand 2, member +0 gets the full value of the subtraction; if Operand 2 is larger than +Operand 1, member 0 gets 2w + Operand 1 - Operand 2, where w is the +component width. + +Member 1 of the result gets 0 if Operand 1 ≥ Operand 2, and gets 1 +otherwise. + + + +#### Example: + +```mlir +%2 = spirv.ISubBorrow %0, %1 : !spirv.struct<(i32, i32)> +%2 = spirv.ISubBorrow %0, %1 : !spirv.struct<(vector<2xi32>, vector<2xi32>)> +``` +""" +function ISubBorrow(operand1::Value, operand2::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.ISubBorrow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ISub` + +Result Type must be a scalar or vector of integer type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same number of components as Result +Type. They must have the same component width as Result Type. + +The resulting value will equal the low-order N bits of the correct +result R, where N is the component width and R is computed with enough +precision to avoid overflow and underflow. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.ISub %0, %1 : i32 +%5 = spirv.ISub %2, %3 : vector<4xi32> + +``` +""" +function ISub( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.ISub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ImageDrefGather` + +Result Type must be a vector of four components of floating-point type +or integer type. Its components must be the same as Sampled Type of the +underlying OpTypeImage (unless that underlying Sampled Type is +OpTypeVoid). It has one component per gathered texel. + +Sampled Image must be an object whose type is OpTypeSampledImage. Its +OpTypeImage must have a Dim of 2D, Cube, or Rect. The MS operand of the +underlying OpTypeImage must be 0. + +Coordinate must be a scalar or vector of floating-point type. It +contains (u[, v] … [, array layer]) as needed by the definition of +Sampled Image. + +Dref is the depth-comparison reference value. It must be a 32-bit +floating-point type scalar. + +Image Operands encodes what operands follow, as per Image Operands. + +#### Example: + +```mlir +%0 = spirv.ImageDrefGather %1 : !spirv.sampled_image>, %2 : vector<4xf32>, %3 : f32 -> vector<4xi32> +%0 = spirv.ImageDrefGather %1 : !spirv.sampled_image>, %2 : vector<4xf32>, %3 : f32 [\"NonPrivateTexel\"] : f32, f32 -> vector<4xi32> +``` +""" +function ImageDrefGather( + sampledimage::Value, + coordinate::Value, + dref::Value, + operand_arguments::Vector{Value}; + result::IR.Type, + imageoperands=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[sampledimage, coordinate, dref, operand_arguments...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(imageoperands) && + push!(_attributes, namedattribute("imageoperands", imageoperands)) + + return IR.create_operation( + "spirv.ImageDrefGather", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Image` + +Result Type must be OpTypeImage. + +Sampled Image must have type OpTypeSampledImage whose Image Type is the +same as Result Type. + + + +#### Example: + +```mlir +%0 = spirv.Image %1 : !spirv.sampled_image> +``` +""" +function Image( + sampledimage::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[sampledimage,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.Image", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ImageQuerySize` + +Result Type must be an integer type scalar or vector. The number of +components must be: + +1 for the 1D and Buffer dimensionalities, + +2 for the 2D, Cube, and Rect dimensionalities, + +3 for the 3D dimensionality, + +plus 1 more if the image type is arrayed. This vector is filled in with +(width [, height] [, elements]) where elements is the number of layers +in an image array or the number of cubes in a cube-map array. + +Image must be an object whose type is OpTypeImage. Its Dim operand must +be one of those listed under Result Type, above. Additionally, if its +Dim is 1D, 2D, 3D, or Cube, it must also have either an MS of 1 or a +Sampled of 0 or 2. There is no implicit level-of-detail consumed by this +instruction. See OpImageQuerySizeLod for querying images having level of +detail. This operation is allowed on an image decorated as NonReadable. +See the client API specification for additional image type restrictions. + + + +#### Example: + +```mlir +%3 = spirv.ImageQuerySize %0 : !spirv.image -> i32 +%4 = spirv.ImageQuerySize %1 : !spirv.image -> vector<2xi32> +%5 = spirv.ImageQuerySize %2 : !spirv.image -> vector<3xi32> +``` +""" +function ImageQuerySize(image::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[image,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.ImageQuerySize", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`InBoundsPtrAccessChain` + + + + + +``` +access-chain-op ::= ssa-id `=` `spirv.InBoundsPtrAccessChain` ssa-use + `[` ssa-use (\',\' ssa-use)* `]` + `:` pointer-type +``` + +#### Example: + +```mlir +func @inbounds_ptr_access_chain(%arg0: !spirv.ptr, %arg1 : i64) -> () { + %0 = spirv.InBoundsPtrAccessChain %arg0[%arg1] : !spirv.ptr, i64 + ... +} +``` +""" +function InBoundsPtrAccessChain( + base_ptr::Value, + element::Value, + indices::Vector{Value}; + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[base_ptr, element, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.InBoundsPtrAccessChain", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`IsInf` + +Result Type must be a scalar or vector of Boolean type. + +x must be a scalar or vector of floating-point type. It must have the +same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.IsInf %0: f32 +%3 = spirv.IsInf %1: vector<4xi32> +``` +""" +function IsInf(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.IsInf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`IsNan` + +Result Type must be a scalar or vector of Boolean type. + +x must be a scalar or vector of floating-point type. It must have the +same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.IsNan %0: f32 +%3 = spirv.IsNan %1: vector<4xi32> +``` +""" +function IsNan(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.IsNan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`KHR_AssumeTrue` + + + + + +``` +assumetruekhr-op ::= `spirv.KHR.AssumeTrue` ssa-use +``` + +#### Example: + +```mlir +spirv.KHR.AssumeTrue %arg +``` +""" +function KHR_AssumeTrue(condition::Value; location=Location()) + _results = IR.Type[] + _operands = Value[condition,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.KHR.AssumeTrue", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`KHR_CooperativeMatrixLength` + +Number of components of a cooperative matrix type accessible to each +invocation when treated as a composite. + +The type attribute must be a cooperative matrix type. + +#### Example: + +``` +%0 = spirv.KHR.CooperativeMatrixLength : + !spirv.coopmatrix<8x16xi32, Subgroup, MatrixA> +``` +""" +function KHR_CooperativeMatrixLength(; + result=nothing::Union{Nothing,IR.Type}, cooperative_matrix_type, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute( + "cooperative_matrix_type", cooperative_matrix_type + ),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.KHR.CooperativeMatrixLength", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`KHR_CooperativeMatrixLoad` + +Load a cooperative matrix through a pointer. + +Result Type is the type of the loaded object. It must be a cooperative +matrix type. + +Pointer is a pointer. Its type must be an OpTypePointer whose Type operand is +a scalar or vector type. If the Shader capability was declared, Pointer must +point into an array and any ArrayStride decoration on Pointer is ignored. + +MemoryLayout specifies how matrix elements are laid out in memory. It must +come from a 32-bit integer constant instruction whose value corresponds to a +Cooperative Matrix Layout. See the Cooperative Matrix Layout table for a +description of the layouts and detailed layout-specific rules. + +Stride further qualifies how matrix elements are laid out in memory. It must +be a scalar integer type and its exact semantics depend on MemoryLayout. + +Memory Operand must be a Memory Operand literal. If not present, it is the +same as specifying None. + +NOTE: In earlier versions of the SPIR-V spec, \'Memory Operand\' was known +as \'Memory Access\'. + +For a given dynamic instance of this instruction, all operands of this +instruction must be the same for all invocations in a given scope instance +(where the scope is the scope the cooperative matrix type was created with). +All invocations in a given scope instance must be active or all must be +inactive. + +TODO: In the SPIR-V spec, `stride` is an optional argument. We should also +support this optionality in the SPIR-V dialect. + +#### Example: + +``` +%0 = spirv.KHR.CooperativeMatrixLoad %ptr, %stride, + : !spirv.ptr, i32 + -> !spirv.KHR.coopmatrix<16x8xi32, Workgroup, MatrixA> + +%1 = spirv.KHR.CooperativeMatrixLoad %ptr, %stride, , + : !spirv.ptr, i64 + -> !spirv.KHR.coopmatrix<8x8xf32, Subgroup, MatrixAcc> +``` +""" +function KHR_CooperativeMatrixLoad( + pointer::Value, + stride::Value; + result::IR.Type, + matrix_layout, + memory_operand=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[pointer, stride] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("matrix_layout", matrix_layout),] + !isnothing(memory_operand) && + push!(_attributes, namedattribute("memory_operand", memory_operand)) + + return IR.create_operation( + "spirv.KHR.CooperativeMatrixLoad", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`KHR_CooperativeMatrixMulAdd` + +Linear-algebraic matrix multiply of A by B and then component-wise add C. +The order of the operations is implementation-dependent. The internal +precision of floating-point operations is defined by the client API. Integer +operations used in the multiplication of A by B are performed at the +precision of the Result Type and the resulting value will equal the +low-order N bits of the correct result R, where N is the result width and R +is computed with enough precision to avoid overflow and underflow if the +SaturatingAccumulation Cooperative Matrix Operand is not present. If the +SaturatingAccumulation Cooperative Matrix Operand is present and overflow or +underflow occurs as part of calculating that intermediate result, the result +of the instruction is undefined. Integer additions of the elements of that +intermediate result with those of C are performed at the precision of Result +Type, are exact, and are saturating if the SaturatingAccumulation +Cooperative Matrix Operand is present, with the signedness of the saturation +being that of the components of Result Type. If the SaturatingAccumulation +Cooperative Matrix Operand is not present then the resulting value will +equal the low-order N bits of the correct result R, where N is the result +width and R is computed with enough precision to avoid overflow and +underflow. + +Result Type must be a cooperative matrix type with M rows and N columns +whose Use must be MatrixAccumulatorKHR. + +A is a cooperative matrix with M rows and K columns whose Use must be +MatrixAKHR. + +B is a cooperative matrix with K rows and N columns whose Use must be +MatrixBKHR. + +C is a cooperative matrix with M rows and N columns whose Use must be +MatrixAccumulatorKHR. + +The values of M, N, and K must be consistent across the result and operands. +This is referred to as an MxNxK matrix multiply. + +A, B, C, and Result Type must have the same scope, and this defines the +scope of the operation. A, B, C, and Result Type need not necessarily have +the same component type, this is defined by the client API. + +If the Component Type of any matrix operand is an integer type, then its +components are treated as signed if the Matrix{A,B,C,Result}SignedComponents +Cooperative Matrix Operand is present and are treated as unsigned otherwise. + +Cooperative Matrix Operands is an optional Cooperative Matrix Operand +literal. If not present, it is the same as specifying the Cooperative Matrix +Operand None. + +For a given dynamic instance of this instruction, all invocations in a given +scope instance must be active or all must be inactive (where the scope is +the scope of the operation). + +``` {.ebnf} +cooperative-matrixmuladd-op ::= ssa-id `=` `spirv.KHR.CooperativeMatrixMulAdd` + ssa-use `,` ssa-use `,` ssa-use + (`<` matrix-operands `>`)? `:` + a-cooperative-matrix-type `,` + b-cooperative-matrix-type `->` + result-cooperative-matrix-type +``` + +#### Example: + +``` +%0 = spirv.KHR.CooperativeMatrixMulAdd %matA, %matB, %matC : + !spirv.coopmatrix<4x4xf32, Subgroup, MatrixA>, + !spirv.coopmatrix<4x4xf32, Subgroup, MatrixB> -> + !spirv.coopmatrix<4x4xf32, Subgroup, MatrixAcc> + +%1 = spirv.KHR.CooperativeMatrixMulAdd %matA, %matB, %matC, : + !spirv.coopmatrix<8x16xi32, Subgroup, MatrixA>, + !spirv.coopmatrix<16x4xi32, Subgroup, MatrixB> -> + !spirv.coopmatrix<8x4xi32, Subgroup, MatrixAcc> +``` +""" +function KHR_CooperativeMatrixMulAdd( + a::Value, + b::Value, + c::Value; + result=nothing::Union{Nothing,IR.Type}, + matrix_operands=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b, c] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(matrix_operands) && + push!(_attributes, namedattribute("matrix_operands", matrix_operands)) + + return IR.create_operation( + "spirv.KHR.CooperativeMatrixMulAdd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`KHR_CooperativeMatrixStore` + +Store a cooperative matrix through a pointer. +Pointer is a pointer. Its type must be an OpTypePointer whose Type operand +is a scalar or vector type. If the Shader capability was declared, Pointer +must point into an array and any ArrayStride decoration on Pointer is +ignored. + +Object is the object to store. Its type must be an +OpTypeCooperativeMatrixKHR. + +MemoryLayout specifies how matrix elements are laid out in memory. It must +come from a 32-bit integer constant instruction whose value corresponds to a +Cooperative Matrix Layout. See the Cooperative Matrix Layout table for a +description of the layouts and detailed layout-specific rules. + +Stride further qualifies how matrix elements are laid out in memory. It must +be a scalar integer type and its exact semantics depend on MemoryLayout. + +Memory Operand must be a Memory Operand literal. If not present, it is the +same as specifying None. + +NOTE: In earlier versions of the SPIR-V spec, \'Memory Operand\' was known +as \'Memory Access\'. + +For a given dynamic instance of this instruction, all operands of this +instruction must be the same for all invocations in a given scope instance +(where the scope is the scope the cooperative matrix type was created with). +All invocations in a given scope instance must be active or all must be +inactive. + +TODO: In the SPIR-V spec, `stride` is an optional argument. We should also +support this optionality in the SPIR-V dialect. + +#### Example: + +``` + spirv.KHR.CooperativeMatrixStore %ptr, %obj, %stride, : + !spirv.ptr, !spirv.coopmatrix<16x8xi32, Workgroup, MatrixA>, i32 + + spirv.KHR.CooperativeMatrixStore %ptr, %obj, %stride, , : + !spirv.ptr, !spirv.coopmatrix<8x8xf32, Subgroup, MatrixAcc>, i64 +``` +""" +function KHR_CooperativeMatrixStore( + pointer::Value, + object::Value, + stride::Value; + matrix_layout, + memory_operand=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[pointer, object, stride] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("matrix_layout", matrix_layout),] + !isnothing(memory_operand) && + push!(_attributes, namedattribute("memory_operand", memory_operand)) + + return IR.create_operation( + "spirv.KHR.CooperativeMatrixStore", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`KHR_SubgroupBallot` + +Computes a bitfield value combining the Predicate value from all invocations +in the current Subgroup that execute the same dynamic instance of this +instruction. The bit is set to one if the corresponding invocation is active +and the predicate is evaluated to true; otherwise, it is set to zero. + +Predicate must be a Boolean type. + +Result Type must be a 4 component vector of 32 bit integer types. + +Result is a set of bitfields where the first invocation is represented in bit +0 of the first vector component and the last (up to SubgroupSize) is the +higher bit number of the last bitmask needed to represent all bits of the +subgroup invocations. + + + +``` +subgroup-ballot-op ::= ssa-id `=` `spirv.KHR.SubgroupBallot` + ssa-use `:` `vector` `<` 4 `x` `i32` `>` +``` + +#### Example: + +```mlir +%0 = spirv.KHR.SubgroupBallot %predicate : vector<4xi32> +``` +""" +function KHR_SubgroupBallot(predicate::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[predicate,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.KHR.SubgroupBallot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Load` + +Result Type is the type of the loaded object. It must be a type with +fixed size; i.e., it cannot be, nor include, any OpTypeRuntimeArray +types. + +Pointer is the pointer to load through. Its type must be an +OpTypePointer whose Type operand is the same as Result Type. + +If present, any Memory Operands must begin with a memory operand +literal. If not present, it is the same as specifying the memory operand +None. + + + +``` +memory-access ::= `\"None\"` | `\"Volatile\"` | `\"Aligned\", ` integer-literal + | `\"NonTemporal\"` + +load-op ::= ssa-id ` = spirv.Load ` storage-class ssa-use + (`[` memory-access `]`)? ` : ` spirv-element-type +``` + +#### Example: + +```mlir +%0 = spirv.Variable : !spirv.ptr +%1 = spirv.Load \"Function\" %0 : f32 +%2 = spirv.Load \"Function\" %0 [\"Volatile\"] : f32 +%3 = spirv.Load \"Function\" %0 [\"Aligned\", 4] : f32 +``` +""" +function Load( + ptr::Value; + value::IR.Type, + memory_access=nothing, + alignment=nothing, + location=Location(), +) + _results = IR.Type[value,] + _operands = Value[ptr,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(memory_access) && + push!(_attributes, namedattribute("memory_access", memory_access)) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + + return IR.create_operation( + "spirv.Load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`LogicalAnd` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 must be the same as Result Type. + +The type of Operand 2 must be the same as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.LogicalAnd %0, %1 : i1 +%2 = spirv.LogicalAnd %0, %1 : vector<4xi1> +``` +""" +function LogicalAnd( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.LogicalAnd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`LogicalEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 must be the same as Result Type. + +The type of Operand 2 must be the same as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.LogicalEqual %0, %1 : i1 +%2 = spirv.LogicalEqual %0, %1 : vector<4xi1> +``` +""" +function LogicalEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.LogicalEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`LogicalNotEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 must be the same as Result Type. + +The type of Operand 2 must be the same as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.LogicalNotEqual %0, %1 : i1 +%2 = spirv.LogicalNotEqual %0, %1 : vector<4xi1> +``` +""" +function LogicalNotEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.LogicalNotEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`LogicalNot` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand must be the same as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.LogicalNot %0 : i1 +%2 = spirv.LogicalNot %0 : vector<4xi1> +``` +""" +function LogicalNot( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.LogicalNot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`LogicalOr` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 must be the same as Result Type. + +The type of Operand 2 must be the same as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.LogicalOr %0, %1 : i1 +%2 = spirv.LogicalOr %0, %1 : vector<4xi1> +``` +""" +function LogicalOr( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.LogicalOr", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mlir_loop` + +SPIR-V can explicitly declare structured control-flow constructs using merge +instructions. These explicitly declare a header block before the control +flow diverges and a merge block where control flow subsequently converges. +These blocks delimit constructs that must nest, and can only be entered +and exited in structured ways. See \"2.11. Structured Control Flow\" of the +SPIR-V spec for more details. + +Instead of having a `spirv.LoopMerge` op to directly model loop merge +instruction for indicating the merge and continue target, we use regions +to delimit the boundary of the loop: the merge target is the next op +following the `spirv.mlir.loop` op and the continue target is the block that +has a back-edge pointing to the entry block inside the `spirv.mlir.loop`\'s region. +This way it\'s easier to discover all blocks belonging to a construct and +it plays nicer with the MLIR system. + +The `spirv.mlir.loop` region should contain at least four blocks: one entry block, +one loop header block, one loop continue block, one loop merge block. +The entry block should be the first block and it should jump to the loop +header block, which is the second block. The loop merge block should be the +last block. The merge block should only contain a `spirv.mlir.merge` op. +The continue block should be the second to last block and it should have a +branch to the loop header block. The loop continue block should be the only +block, except the entry block, branching to the header block. +""" +function mlir_loop(; loop_control, body::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("loop_control", loop_control),] + + return IR.create_operation( + "spirv.mlir.loop", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`MatrixTimesMatrix` + +Result Type must be an OpTypeMatrix whose Column Type is a vector of +floating-point type. + +LeftMatrix must be a matrix whose Column Type is the same as the Column +Type in Result Type. + +RightMatrix must be a matrix with the same Component Type as the +Component Type in Result Type. Its number of columns must equal the +number of columns in Result Type. Its columns must have the same number +of components as the number of columns in LeftMatrix. + +#### Example: + +```mlir +%0 = spirv.MatrixTimesMatrix %matrix_1, %matrix_2 : + !spirv.matrix<4 x vector<3xf32>>, !spirv.matrix<3 x vector<4xf32>> -> + !spirv.matrix<4 x vector<4xf32>> +``` +""" +function MatrixTimesMatrix( + leftmatrix::Value, rightmatrix::Value; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[leftmatrix, rightmatrix] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.MatrixTimesMatrix", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`MatrixTimesScalar` + +Result Type must be a matrix type with a float component type. + +The type of Matrix must be the same as Result Type. Each component in +each column in Matrix is multiplied by Scalar. + +Scalar must have the same type as the Component Type in Result Type. + +#### Example: + +```mlir +%0 = spirv.MatrixTimesScalar %matrix, %scalar : +!spirv.matrix<3 x vector<3xf32>>, f32 -> !spirv.matrix<3 x vector<3xf32>> + +``` +""" +function MatrixTimesScalar( + matrix::Value, + scalar::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[matrix, scalar] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.MatrixTimesScalar", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`MemoryBarrier` + +Ensures that memory accesses issued before this instruction will be +observed before memory accesses issued after this instruction. This +control is ensured only for memory accesses issued by this invocation +and observed by another invocation executing within Memory scope. If the +Vulkan memory model is declared, this ordering only applies to memory +accesses that use the NonPrivatePointer memory operand or +NonPrivateTexel image operand. + +Semantics declares what kind of memory is being controlled and what kind +of control to apply. + +To execute both a memory barrier and a control barrier, see +OpControlBarrier. + +#### Example: + +```mlir +spirv.MemoryBarrier \"Device\", \"Acquire|UniformMemory\" +``` +""" +function MemoryBarrier(; memory_scope, memory_semantics, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_scope", memory_scope), + namedattribute("memory_semantics", memory_semantics), + ] + + return IR.create_operation( + "spirv.MemoryBarrier", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mlir_merge` + +We use `spirv.mlir.selection`/`spirv.mlir.loop` for modelling structured selection/loop. +This op is a terminator used inside their regions to mean jumping to the +merge point, which is the next op following the `spirv.mlir.selection` or +`spirv.mlir.loop` op. This op does not have a corresponding instruction in the +SPIR-V binary format; it\'s solely for structural purpose. +""" +function mlir_merge(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.mlir.merge", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`module_` + +This op defines a SPIR-V module using a MLIR region. The region contains +one block. Module-level operations, including functions definitions, +are all placed in this block. + +Using an op with a region to define a SPIR-V module enables \"embedding\" +SPIR-V modules in other dialects in a clean manner: this op guarantees +the validity and serializability of a SPIR-V module and thus serves as +a clear-cut boundary. + +This op takes no operands and generates no results. This op should not +implicitly capture values from the enclosing environment. + +This op has only one region, which only contains one block. The block +has no terminator. + + + +``` +addressing-model ::= `Logical` | `Physical32` | `Physical64` | ... +memory-model ::= `Simple` | `GLSL450` | `OpenCL` | `Vulkan` | ... +spv-module-op ::= `spirv.module` addressing-model memory-model + (requires spirv-vce-attribute)? + (`attributes` attribute-dict)? + region +``` + +#### Example: + +```mlir +spirv.module Logical GLSL450 {} + +spirv.module Logical Vulkan + requires #spirv.vce + attributes { some_additional_attr = ... } { + spirv.func @do_nothing() -> () { + spirv.Return + } +} +``` +""" +function module_(; + addressing_model, + memory_model, + vce_triple=nothing, + sym_name=nothing, + region_0::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[region_0,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("addressing_model", addressing_model), + namedattribute("memory_model", memory_model), + ] + !isnothing(vce_triple) && push!(_attributes, namedattribute("vce_triple", vce_triple)) + !isnothing(sym_name) && push!(_attributes, namedattribute("sym_name", sym_name)) + + return IR.create_operation( + "spirv.module", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Not` + +Results are computed per component, and within each component, per bit. + +Result Type must be a scalar or vector of integer type. + +Operand\'s type must be a scalar or vector of integer type. It must +have the same number of components as Result Type. The component width +must equal the component width in Result Type. + +#### Example: + +```mlir +%2 = spirv.Not %0 : i32 +%3 = spirv.Not %1 : vector<4xi32> +``` +""" +function Not(operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.Not", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`Ordered` + +Result Type must be a scalar or vector of Boolean type. + +x must be a scalar or vector of floating-point type. It must have the +same number of components as Result Type. + +y must have the same type as x. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.Ordered %0, %1 : f32 +%5 = spirv.Ordered %2, %3 : vector<4xf32> +``` +""" +function Ordered( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.Ordered", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`PtrAccessChain` + +Element is used to do an initial dereference of Base: Base is treated as +the address of an element in an array, and a new element address is +computed from Base and Element to become the OpAccessChain Base to +dereference as per OpAccessChain. This computed Base has the same type +as the originating Base. + +To compute the new element address, Element is treated as a signed count +of elements E, relative to the original Base element B, and the address +of element B + E is computed using enough precision to avoid overflow +and underflow. For objects in the Uniform, StorageBuffer, or +PushConstant storage classes, the element\'s address or location is +calculated using a stride, which will be the Base-type\'s Array Stride if +the Base type is decorated with ArrayStride. For all other objects, the +implementation calculates the element\'s address or location. + +With one exception, undefined behavior results when B + E is not an +element in the same array (same innermost array, if array types are +nested) as B. The exception being when B + E = L, where L is the length +of the array: the address computation for element L is done with the +same stride as any other B + E computation that stays within the array. + +Note: If Base is typed to be a pointer to an array and the desired +operation is to select an element of that array, OpAccessChain should be +directly used, as its first Index selects the array element. + + + +``` +[access-chain-op ::= ssa-id `=` `spirv.PtrAccessChain` ssa-use + `[` ssa-use (\',\' ssa-use)* `]` + `:` pointer-type +``` + +#### Example: + +```mlir +func @ptr_access_chain(%arg0: !spirv.ptr, %arg1 : i64) -> () { + %0 = spirv.PtrAccessChain %arg0[%arg1] : !spirv.ptr, i64 + ... +} +``` +""" +function PtrAccessChain( + base_ptr::Value, + element::Value, + indices::Vector{Value}; + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[base_ptr, element, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.PtrAccessChain", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`PtrCastToGeneric` + +Result Type must be an OpTypePointer. Its Storage Class must be Generic. + +Pointer must point to the Workgroup, CrossWorkgroup, or Function Storage +Class. + +Result Type and Pointer must point to the same type. + + + +#### Example: + +```mlir +%1 = spirv.PtrCastToGenericOp %0 : !spirv.ptr to + !spirv.ptr +``` +""" +function PtrCastToGeneric(pointer::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[pointer,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.PtrCastToGeneric", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mlir_referenceof` + +Specialization constants in module scope are defined using symbol names. +This op generates an SSA value that can be used to refer to the symbol +within function scope for use in ops that expect an SSA value. +This operation has no corresponding SPIR-V instruction; it\'s merely used +for modelling purpose in the SPIR-V dialect. This op\'s return type is +the same as the specialization constant. + +#### Example: + +```mlir +%0 = spirv.mlir.referenceof @spec_const : f32 +``` + +TODO Add support for composite specialization constants. +""" +function mlir_referenceof(; reference::IR.Type, spec_const, location=Location()) + _results = IR.Type[reference,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("spec_const", spec_const),] + + return IR.create_operation( + "spirv.mlir.referenceof", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Return` + +This instruction must be the last instruction in a block. + +#### Example: + +```mlir +spirv.Return +``` +""" +function Return(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.Return", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ReturnValue` + +Value is the value returned, by copy, and must match the Return Type +operand of the OpTypeFunction type of the OpFunction body this return +instruction is in. + +This instruction must be the last instruction in a block. + +#### Example: + +```mlir +spirv.ReturnValue %0 : f32 +``` +""" +function ReturnValue(value::Value; location=Location()) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.ReturnValue", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`SConvert` + +Result Type must be a scalar or vector of integer type. + +Signed Value must be a scalar or vector of integer type. It must have +the same number of components as Result Type. The component width +cannot equal the component width in Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%1 = spirv.SConvertOp %0 : i32 to i64 +%3 = spirv.SConvertOp %2 : vector<3xi32> to vector<3xi64> +``` +""" +function SConvert(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.SConvert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`SDiv` + +Result Type must be a scalar or vector of integer type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same number of components as Result +Type. They must have the same component width as Result Type. + +Results are computed per component. The resulting value is undefined +if Operand 2 is 0. + +#### Example: + +```mlir +%4 = spirv.SDiv %0, %1 : i32 +%5 = spirv.SDiv %2, %3 : vector<4xi32> + +``` +""" +function SDiv( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.SDiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`SDotAccSat` + +Result Type must be an integer type whose Width must be greater than or +equal to that of the components of Vector 1 and Vector 2. + +Vector 1 and Vector 2 must have the same type. + +Vector 1 and Vector 2 must be either 32-bit integers (enabled by the +DotProductInput4x8BitPacked capability) or vectors of integer type +(enabled by the DotProductInput4x8Bit or DotProductInputAll capability). + +The type of Accumulator must be the same as Result Type. + +When Vector 1 and Vector 2 are scalar integer types, Packed Vector +Format must be specified to select how the integers are to be +interpreted as vectors. + +All components of the input vectors are sign-extended to the bit width +of the result\'s type. The sign-extended input vectors are then +multiplied component-wise and all components of the vector resulting +from the component-wise multiplication are added together. Finally, the +resulting sum is added to the input accumulator. This final addition is +saturating. + +If any of the multiplications or additions, with the exception of the +final accumulation, overflow or underflow, the result of the instruction +is undefined. + + + +#### Example: + +```mlir +%r = spirv.SDotAccSat %a, %b, %acc, : i32 -> i32 +%r = spirv.SDotAccSat %a, %b, %acc, : i32 -> i64 +%r = spirv.SDotAccSat %a, %b, %acc : vector<4xi8> -> i32 +``` +""" +function SDotAccSat( + vector1::Value, + vector2::Value, + accumulator::Value; + result=nothing::Union{Nothing,IR.Type}, + format=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vector1, vector2, accumulator] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(format) && push!(_attributes, namedattribute("format", format)) + + return IR.create_operation( + "spirv.SDotAccSat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`SDot` + +Result Type must be an integer type whose Width must be greater than or +equal to that of the components of Vector 1 and Vector 2. + +Vector 1 and Vector 2 must have the same type. + +Vector 1 and Vector 2 must be either 32-bit integers (enabled by the +DotProductInput4x8BitPacked capability) or vectors of integer type +(enabled by the DotProductInput4x8Bit or DotProductInputAll capability). + +When Vector 1 and Vector 2 are scalar integer types, Packed Vector +Format must be specified to select how the integers are to be +interpreted as vectors. + +All components of the input vectors are sign-extended to the bit width +of the result\'s type. The sign-extended input vectors are then +multiplied component-wise and all components of the vector resulting +from the component-wise multiplication are added together. The resulting +value will equal the low-order N bits of the correct result R, where N +is the result width and R is computed with enough precision to avoid +overflow and underflow. + + + +#### Example: + +```mlir +%r = spirv.SDot %a, %b, : i32 -> i32 +%r = spirv.SDot %a, %b, : i32 -> i64 +%r = spirv.SDot %a, %b : vector<4xi8> -> i32 +``` +""" +function SDot( + vector1::Value, vector2::Value; result::IR.Type, format=nothing, location=Location() +) + _results = IR.Type[result,] + _operands = Value[vector1, vector2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(format) && push!(_attributes, namedattribute("format", format)) + + return IR.create_operation( + "spirv.SDot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`SGreaterThanEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same component width, and they must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.SGreaterThanEqual %0, %1 : i32 +%5 = spirv.SGreaterThanEqual %2, %3 : vector<4xi32> +``` +""" +function SGreaterThanEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.SGreaterThanEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`SGreaterThan` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same component width, and they must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.SGreaterThan %0, %1 : i32 +%5 = spirv.SGreaterThan %2, %3 : vector<4xi32> + +``` +""" +function SGreaterThan( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.SGreaterThan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`SLessThanEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same component width, and they must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.SLessThanEqual %0, %1 : i32 +%5 = spirv.SLessThanEqual %2, %3 : vector<4xi32> +``` +""" +function SLessThanEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.SLessThanEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`SLessThan` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same component width, and they must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.SLessThan %0, %1 : i32 +%5 = spirv.SLessThan %2, %3 : vector<4xi32> + +``` +""" +function SLessThan( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.SLessThan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`SMod` + +Result Type must be a scalar or vector of integer type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same number of components as Result +Type. They must have the same component width as Result Type. + +Results are computed per component. The resulting value is undefined +if Operand 2 is 0. Otherwise, the result is the remainder r of Operand +1 divided by Operand 2 where if r ≠ 0, the sign of r is the same as the +sign of Operand 2. + +#### Example: + +```mlir +%4 = spirv.SMod %0, %1 : i32 +%5 = spirv.SMod %2, %3 : vector<4xi32> + +``` +""" +function SMod( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.SMod", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`SMulExtended` + +Result Type must be from OpTypeStruct. The struct must have two +members, and the two members must be the same type. The member type +must be a scalar or vector of integer type. + +Operand 1 and Operand 2 must have the same type as the members of Result +Type. These are consumed as signed integers. + +Results are computed per component. + +Member 0 of the result gets the low-order bits of the multiplication. + +Member 1 of the result gets the high-order bits of the multiplication. + + + +#### Example: + +```mlir +%2 = spirv.SMulExtended %0, %1 : !spirv.struct<(i32, i32)> +%2 = spirv.SMulExtended %0, %1 : !spirv.struct<(vector<2xi32>, vector<2xi32>)> +``` +""" +function SMulExtended( + operand1::Value, operand2::Value; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.SMulExtended", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`SNegate` + +Result Type must be a scalar or vector of integer type. + +Operand\'s type must be a scalar or vector of integer type. It must +have the same number of components as Result Type. The component width +must equal the component width in Result Type. + + Results are computed per component. + + + +#### Example: + +```mlir +%1 = spirv.SNegate %0 : i32 +%3 = spirv.SNegate %2 : vector<4xi32> +``` +""" +function SNegate( + operand::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.SNegate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`SRem` + +Result Type must be a scalar or vector of integer type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same number of components as Result +Type. They must have the same component width as Result Type. + +Results are computed per component. The resulting value is undefined +if Operand 2 is 0. Otherwise, the result is the remainder r of Operand +1 divided by Operand 2 where if r ≠ 0, the sign of r is the same as the +sign of Operand 1. + +#### Example: + +```mlir +%4 = spirv.SRem %0, %1 : i32 +%5 = spirv.SRem %2, %3 : vector<4xi32> + +``` +""" +function SRem( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.SRem", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`SUDotAccSat` + +Result Type must be an integer type whose Width must be greater than or +equal to that of the components of Vector 1 and Vector 2. + +Vector 1 and Vector 2 must be either 32-bit integers (enabled by the +DotProductInput4x8BitPacked capability) or vectors of integer type with +the same number of components and same component Width (enabled by the +DotProductInput4x8Bit or DotProductInputAll capability). When Vector 1 +and Vector 2 are vectors, the components of Vector 2 must have a +Signedness of 0. + +The type of Accumulator must be the same as Result Type. + +When Vector 1 and Vector 2 are scalar integer types, Packed Vector +Format must be specified to select how the integers are to be +interpreted as vectors. + +All components of Vector 1 are sign-extended to the bit width of the +result\'s type. All components of Vector 2 are zero-extended to the bit +width of the result\'s type. The sign- or zero-extended input vectors are +then multiplied component-wise and all components of the vector +resulting from the component-wise multiplication are added together. +Finally, the resulting sum is added to the input accumulator. This final +addition is saturating. + +If any of the multiplications or additions, with the exception of the +final accumulation, overflow or underflow, the result of the instruction +is undefined. + + + +#### Example: + +```mlir +%r = spirv.SUDotAccSat %a, %b, %acc, : i32 -> i32 +%r = spirv.SUDotAccSat %a, %b, %acc, : i32 -> i64 +%r = spirv.SUDotAccSat %a, %b, %acc : vector<4xi8> -> i32 +``` +""" +function SUDotAccSat( + vector1::Value, + vector2::Value, + accumulator::Value; + result=nothing::Union{Nothing,IR.Type}, + format=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vector1, vector2, accumulator] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(format) && push!(_attributes, namedattribute("format", format)) + + return IR.create_operation( + "spirv.SUDotAccSat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`SUDot` + +Result Type must be an integer type whose Width must be greater than or +equal to that of the components of Vector 1 and Vector 2. + +Vector 1 and Vector 2 must be either 32-bit integers (enabled by the +DotProductInput4x8BitPacked capability) or vectors of integer type with +the same number of components and same component Width (enabled by the +DotProductInput4x8Bit or DotProductInputAll capability). When Vector 1 +and Vector 2 are vectors, the components of Vector 2 must have a +Signedness of 0. + +When Vector 1 and Vector 2 are scalar integer types, Packed Vector +Format must be specified to select how the integers are to be +interpreted as vectors. + +All components of Vector 1 are sign-extended to the bit width of the +result\'s type. All components of Vector 2 are zero-extended to the bit +width of the result\'s type. The sign- or zero-extended input vectors are +then multiplied component-wise and all components of the vector +resulting from the component-wise multiplication are added together. The +resulting value will equal the low-order N bits of the correct result R, +where N is the result width and R is computed with enough precision to +avoid overflow and underflow. + + + +#### Example: + +```mlir +%r = spirv.SUDot %a, %b, : i32 -> i32 +%r = spirv.SUDot %a, %b, : i32 -> i64 +%r = spirv.SUDot %a, %b : vector<4xi8> -> i32 +``` +""" +function SUDot( + vector1::Value, vector2::Value; result::IR.Type, format=nothing, location=Location() +) + _results = IR.Type[result,] + _operands = Value[vector1, vector2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(format) && push!(_attributes, namedattribute("format", format)) + + return IR.create_operation( + "spirv.SUDot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Select` + +Before version 1.4, Result Type must be a pointer, scalar, or vector. + +The types of Object 1 and Object 2 must be the same as Result Type. + +Condition must be a scalar or vector of Boolean type. + +If Condition is a scalar and true, the result is Object 1. If Condition +is a scalar and false, the result is Object 2. + +If Condition is a vector, Result Type must be a vector with the same +number of components as Condition and the result is a mix of Object 1 +and Object 2: When a component of Condition is true, the corresponding +component in the result is taken from Object 1, otherwise it is taken +from Object 2. + +#### Example: + +```mlir +%3 = spirv.Select %0, %1, %2 : i1, f32 +%3 = spirv.Select %0, %1, %2 : i1, vector<3xi32> +%3 = spirv.Select %0, %1, %2 : vector<3xi1>, vector<3xf32> +``` +""" +function Select( + condition::Value, + true_value::Value, + false_value::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[condition, true_value, false_value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.Select", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mlir_selection` + +SPIR-V can explicitly declare structured control-flow constructs using merge +instructions. These explicitly declare a header block before the control +flow diverges and a merge block where control flow subsequently converges. +These blocks delimit constructs that must nest, and can only be entered +and exited in structured ways. See \"2.11. Structured Control Flow\" of the +SPIR-V spec for more details. + +Instead of having a `spirv.SelectionMerge` op to directly model selection +merge instruction for indicating the merge target, we use regions to delimit +the boundary of the selection: the merge target is the next op following the +`spirv.mlir.selection` op. This way it\'s easier to discover all blocks belonging to +the selection and it plays nicer with the MLIR system. + +The `spirv.mlir.selection` region should contain at least two blocks: one selection +header block, and one selection merge. The selection header block should be +the first block. The selection merge block should be the last block. +The merge block should only contain a `spirv.mlir.merge` op. +""" +function mlir_selection(; selection_control, body::Region, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("selection_control", selection_control),] + + return IR.create_operation( + "spirv.mlir.selection", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ShiftLeftLogical` + +Result Type must be a scalar or vector of integer type. + +The type of each Base and Shift must be a scalar or vector of integer +type. Base and Shift must have the same number of components. The +number of components and bit width of the type of Base must be the same +as in Result Type. + +Shift is treated as unsigned. The result is undefined if Shift is +greater than or equal to the bit width of the components of Base. + +The number of components and bit width of Result Type must match those +Base type. All types must be integer types. + +Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.ShiftLeftLogical %0, %1 : i32, i16 +%5 = spirv.ShiftLeftLogical %3, %4 : vector<3xi32>, vector<3xi16> +``` +""" +function ShiftLeftLogical( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.ShiftLeftLogical", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ShiftRightArithmetic` + +Result Type must be a scalar or vector of integer type. + +The type of each Base and Shift must be a scalar or vector of integer +type. Base and Shift must have the same number of components. The +number of components and bit width of the type of Base must be the same +as in Result Type. + +Shift is treated as unsigned. The result is undefined if Shift is +greater than or equal to the bit width of the components of Base. + +Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.ShiftRightArithmetic %0, %1 : i32, i16 +%5 = spirv.ShiftRightArithmetic %3, %4 : vector<3xi32>, vector<3xi16> +``` +""" +function ShiftRightArithmetic( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.ShiftRightArithmetic", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ShiftRightLogical` + +Result Type must be a scalar or vector of integer type. + +The type of each Base and Shift must be a scalar or vector of integer +type. Base and Shift must have the same number of components. The +number of components and bit width of the type of Base must be the same +as in Result Type. + +Shift is consumed as an unsigned integer. The result is undefined if +Shift is greater than or equal to the bit width of the components of +Base. + +Results are computed per component. + +#### Example: + +```mlir +%2 = spirv.ShiftRightLogical %0, %1 : i32, i16 +%5 = spirv.ShiftRightLogical %3, %4 : vector<3xi32>, vector<3xi16> +``` +""" +function ShiftRightLogical( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.ShiftRightLogical", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`SpecConstantComposite` + +This op declares a SPIR-V composite specialization constant. This covers +the `OpSpecConstantComposite` SPIR-V instruction. Scalar constants are +covered by `spirv.SpecConstant`. + +A constituent of a spec constant composite can be: +- A symbol referring of another spec constant. +- The SSA ID of a non-specialization constant (i.e. defined through + `spirv.SpecConstant`). +- The SSA ID of a `spirv.Undef`. + +``` +spv-spec-constant-composite-op ::= `spirv.SpecConstantComposite` symbol-ref-id ` (` + symbol-ref-id (`, ` symbol-ref-id)* + `) :` composite-type +``` + + where `composite-type` is some non-scalar type that can be represented in the `spv` + dialect: `spirv.struct`, `spirv.array`, or `vector`. + + #### Example: + + ```mlir + spirv.SpecConstant @sc1 = 1 : i32 + spirv.SpecConstant @sc2 = 2.5 : f32 + spirv.SpecConstant @sc3 = 3.5 : f32 + spirv.SpecConstantComposite @scc (@sc1, @sc2, @sc3) : !spirv.struct + ``` + +TODO Add support for constituents that are: +- regular constants. +- undef. +- spec constant composite. +""" +function SpecConstantComposite(; type, sym_name, constituents, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("type", type), + namedattribute("sym_name", sym_name), + namedattribute("constituents", constituents), + ] + + return IR.create_operation( + "spirv.SpecConstantComposite", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`SpecConstant` + +This op declares a SPIR-V scalar specialization constant. SPIR-V has +multiple constant instructions covering different scalar types: + +* `OpSpecConstantTrue` and `OpSpecConstantFalse` for boolean constants +* `OpSpecConstant` for scalar constants + +Similar as `spirv.Constant`, this op represents all of the above cases. +`OpSpecConstantComposite` and `OpSpecConstantOp` are modelled with +separate ops. + + + +``` +spv-spec-constant-op ::= `spirv.SpecConstant` symbol-ref-id + `spec_id(` integer `)` + `=` attribute-value (`:` spirv-type)? +``` + +where `spec_id` specifies the SPIR-V SpecId decoration associated with +the op. + +#### Example: + +```mlir +spirv.SpecConstant @spec_const1 = true +spirv.SpecConstant @spec_const2 spec_id(5) = 42 : i32 +``` +""" +function SpecConstant(; sym_name, default_value, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("default_value", default_value) + ] + + return IR.create_operation( + "spirv.SpecConstant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`SpecConstantOperation` + +This op declares a SPIR-V specialization constant that results from +doing an operation on other constants (specialization or otherwise). + +In the `spv` dialect, this op is modelled as follows: + +``` +spv-spec-constant-operation-op ::= `spirv.SpecConstantOperation` `wraps` + generic-spirv-op `:` function-type +``` + +In particular, an `spirv.SpecConstantOperation` contains exactly one +region. In turn, that region, contains exactly 2 instructions: +- One of SPIR-V\'s instructions that are allowed within an +OpSpecConstantOp. +- An `spirv.mlir.yield` instruction as the terminator. + +The following SPIR-V instructions are valid: +- OpSConvert, +- OpUConvert, +- OpFConvert, +- OpSNegate, +- OpNot, +- OpIAdd, +- OpISub, +- OpIMul, +- OpUDiv, +- OpSDiv, +- OpUMod, +- OpSRem, +- OpSMod +- OpShiftRightLogical, +- OpShiftRightArithmetic, +- OpShiftLeftLogical +- OpBitwiseOr, +- OpBitwiseXor, +- OpBitwiseAnd +- OpVectorShuffle, +- OpCompositeExtract, +- OpCompositeInsert +- OpLogicalOr, +- OpLogicalAnd, +- OpLogicalNot, +- OpLogicalEqual, +- OpLogicalNotEqual +- OpSelect +- OpIEqual, +- OpINotEqual +- OpULessThan, +- OpSLessThan +- OpUGreaterThan, +- OpSGreaterThan +- OpULessThanEqual, +- OpSLessThanEqual +- OpUGreaterThanEqual, +- OpSGreaterThanEqual + +TODO Add capability-specific ops when supported. + +#### Example: +```mlir +%0 = spirv.Constant 1: i32 +%1 = spirv.Constant 1: i32 + +%2 = spirv.SpecConstantOperation wraps \"spirv.IAdd\"(%0, %1) : (i32, i32) -> i32 +``` +""" +function SpecConstantOperation(; result::IR.Type, body::Region, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.SpecConstantOperation", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Store` + +Pointer is the pointer to store through. Its type must be an +OpTypePointer whose Type operand is the same as the type of Object. + +Object is the object to store. + +If present, any Memory Operands must begin with a memory operand +literal. If not present, it is the same as specifying the memory operand +None. + + + +``` +store-op ::= `spirv.Store ` storage-class ssa-use `, ` ssa-use `, ` + (`[` memory-access `]`)? `:` spirv-element-type +``` + +#### Example: + +```mlir +%0 = spirv.Variable : !spirv.ptr +%1 = spirv.FMul ... : f32 +spirv.Store \"Function\" %0, %1 : f32 +spirv.Store \"Function\" %0, %1 [\"Volatile\"] : f32 +spirv.Store \"Function\" %0, %1 [\"Aligned\", 4] : f32 +``` +""" +function Store( + ptr::Value, value::Value; memory_access=nothing, alignment=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[ptr, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(memory_access) && + push!(_attributes, namedattribute("memory_access", memory_access)) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + + return IR.create_operation( + "spirv.Store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Transpose` + +Result Type must be an OpTypeMatrix. + +Matrix must be an object of type OpTypeMatrix. The number of columns and +the column size of Matrix must be the reverse of those in Result Type. +The types of the scalar components in Matrix and Result Type must be the +same. + +Matrix must have of type of OpTypeMatrix. + +#### Example: + +```mlir +%0 = spirv.Transpose %matrix: !spirv.matrix<2 x vector<3xf32>> -> +!spirv.matrix<3 x vector<2xf32>> +``` +""" +function Transpose(matrix::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[matrix,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.Transpose", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`UConvert` + +Result Type must be a scalar or vector of integer type, whose Signedness +operand is 0. + +Unsigned Value must be a scalar or vector of integer type. It must have +the same number of components as Result Type. The component width +cannot equal the component width in Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%1 = spirv.UConvertOp %0 : i32 to i64 +%3 = spirv.UConvertOp %2 : vector<3xi32> to vector<3xi64> +``` +""" +function UConvert(operand::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.UConvert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`UDiv` + +Result Type must be a scalar or vector of integer type, whose Signedness +operand is 0. + +The types of Operand 1 and Operand 2 both must be the same as Result +Type. + +Results are computed per component. The resulting value is undefined +if Operand 2 is 0. + +#### Example: + +```mlir +%4 = spirv.UDiv %0, %1 : i32 +%5 = spirv.UDiv %2, %3 : vector<4xi32> +``` +""" +function UDiv( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.UDiv", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`UDotAccSat` + +Result Type must be an integer type with Signedness of 0 whose Width +must be greater than or equal to that of the components of Vector 1 and +Vector 2. + +Vector 1 and Vector 2 must have the same type. + +Vector 1 and Vector 2 must be either 32-bit integers (enabled by the +DotProductInput4x8BitPacked capability) or vectors of integer type with +Signedness of 0 (enabled by the DotProductInput4x8Bit or +DotProductInputAll capability). + +The type of Accumulator must be the same as Result Type. + +When Vector 1 and Vector 2 are scalar integer types, Packed Vector +Format must be specified to select how the integers are to be +interpreted as vectors. + +All components of the input vectors are zero-extended to the bit width +of the result\'s type. The zero-extended input vectors are then +multiplied component-wise and all components of the vector resulting +from the component-wise multiplication are added together. Finally, the +resulting sum is added to the input accumulator. This final addition is +saturating. + +If any of the multiplications or additions, with the exception of the +final accumulation, overflow or underflow, the result of the instruction +is undefined. + + + +#### Example: + +```mlir +%r = spirv.UDotAccSat %a, %b, %acc, : i32 -> i32 +%r = spirv.UDotAccSat %a, %b, %acc, : i32 -> i64 +%r = spirv.UDotAccSat %a, %b, %acc : vector<4xi8> -> i32 +``` +""" +function UDotAccSat( + vector1::Value, + vector2::Value, + accumulator::Value; + result=nothing::Union{Nothing,IR.Type}, + format=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vector1, vector2, accumulator] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(format) && push!(_attributes, namedattribute("format", format)) + + return IR.create_operation( + "spirv.UDotAccSat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`UDot` + +Result Type must be an integer type with Signedness of 0 whose Width +must be greater than or equal to that of the components of Vector 1 and +Vector 2. + +Vector 1 and Vector 2 must have the same type. + +Vector 1 and Vector 2 must be either 32-bit integers (enabled by the +DotProductInput4x8BitPacked capability) or vectors of integer type with +Signedness of 0 (enabled by the DotProductInput4x8Bit or +DotProductInputAll capability). + +When Vector 1 and Vector 2 are scalar integer types, Packed Vector +Format must be specified to select how the integers are to be +interpreted as vectors. + +All components of the input vectors are zero-extended to the bit width +of the result\'s type. The zero-extended input vectors are then +multiplied component-wise and all components of the vector resulting +from the component-wise multiplication are added together. The resulting +value will equal the low-order N bits of the correct result R, where N +is the result width and R is computed with enough precision to avoid +overflow and underflow. + + + +#### Example: + +```mlir +%r = spirv.UDot %a, %b, : i32 -> i32 +%r = spirv.UDot %a, %b, : i32 -> i64 +%r = spirv.UDot %a, %b : vector<4xi8> -> i32 +``` +""" +function UDot( + vector1::Value, vector2::Value; result::IR.Type, format=nothing, location=Location() +) + _results = IR.Type[result,] + _operands = Value[vector1, vector2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(format) && push!(_attributes, namedattribute("format", format)) + + return IR.create_operation( + "spirv.UDot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`UGreaterThanEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same component width, and they must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.UGreaterThanEqual %0, %1 : i32 +%5 = spirv.UGreaterThanEqual %2, %3 : vector<4xi32> +``` +""" +function UGreaterThanEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.UGreaterThanEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`UGreaterThan` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same component width, and they must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.UGreaterThan %0, %1 : i32 +%5 = spirv.UGreaterThan %2, %3 : vector<4xi32> +``` +""" +function UGreaterThan( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.UGreaterThan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ULessThanEqual` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same component width, and they must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.ULessThanEqual %0, %1 : i32 +%5 = spirv.ULessThanEqual %2, %3 : vector<4xi32> +``` +""" +function ULessThanEqual( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.ULessThanEqual", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`ULessThan` + +Result Type must be a scalar or vector of Boolean type. + +The type of Operand 1 and Operand 2 must be a scalar or vector of +integer type. They must have the same component width, and they must +have the same number of components as Result Type. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.ULessThan %0, %1 : i32 +%5 = spirv.ULessThan %2, %3 : vector<4xi32> +``` +""" +function ULessThan( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.ULessThan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`UMod` + +Result Type must be a scalar or vector of integer type, whose Signedness +operand is 0. + +The types of Operand 1 and Operand 2 both must be the same as Result +Type. + +Results are computed per component. The resulting value is undefined +if Operand 2 is 0. + +#### Example: + +```mlir +%4 = spirv.UMod %0, %1 : i32 +%5 = spirv.UMod %2, %3 : vector<4xi32> +``` +""" +function UMod( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.UMod", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`UMulExtended` + +Result Type must be from OpTypeStruct. The struct must have two +members, and the two members must be the same type. The member type +must be a scalar or vector of integer type, whose Signedness operand is +0. + +Operand 1 and Operand 2 must have the same type as the members of Result +Type. These are consumed as unsigned integers. + +Results are computed per component. + +Member 0 of the result gets the low-order bits of the multiplication. + +Member 1 of the result gets the high-order bits of the multiplication. + + + +#### Example: + +```mlir +%2 = spirv.UMulExtended %0, %1 : !spirv.struct<(i32, i32)> +%2 = spirv.UMulExtended %0, %1 : !spirv.struct<(vector<2xi32>, vector<2xi32>)> +``` +""" +function UMulExtended( + operand1::Value, operand2::Value; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.UMulExtended", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Undef` + +Result Type is the type of object to make. + +Each consumption of Result yields an arbitrary, possibly different +bit pattern or abstract value resulting in possibly different concrete, +abstract, or opaque values. + +#### Example: + +```mlir +%0 = spirv.Undef : f32 +%1 = spirv.Undef : !spirv.struct>> +``` +""" +function Undef(; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.Undef", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Unordered` + +Result Type must be a scalar or vector of Boolean type. + +x must be a scalar or vector of floating-point type. It must have the +same number of components as Result Type. + +y must have the same type as x. + +Results are computed per component. + +#### Example: + +```mlir +%4 = spirv.Unordered %0, %1 : f32 +%5 = spirv.Unordered %2, %3 : vector<4xf32> +``` +""" +function Unordered( + operand1::Value, + operand2::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand1, operand2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.Unordered", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`Unreachable` + +This instruction must be the last instruction in a block. +""" +function Unreachable(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.Unreachable", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`Variable` + +Result Type must be an OpTypePointer. Its Type operand is the type of +object in memory. + +Storage Class is the Storage Class of the memory holding the object. +Since the op is used to model function-level variables, the storage class +must be the `Function` Storage Class. + +Initializer is optional. If Initializer is present, it will be the +initial value of the variable\'s memory content. Initializer must be an + from a constant instruction or a global (module scope) OpVariable +instruction. Initializer must have the same type as the type pointed to +by Result Type. + +From `SPV_KHR_physical_storage_buffer`: +If an OpVariable\'s pointee type is a pointer (or array of pointers) in +PhysicalStorageBuffer storage class, then the variable must be decorated +with exactly one of AliasedPointer or RestrictPointer. + + + +``` +variable-op ::= ssa-id `=` `spirv.Variable` (`init(` ssa-use `)`)? + attribute-dict? `:` spirv-pointer-type +``` + +where `init` specifies initializer. + +#### Example: + +```mlir +%0 = spirv.Constant ... + +%1 = spirv.Variable : !spirv.ptr +%2 = spirv.Variable init(%0): !spirv.ptr + +%3 = spirv.Variable {aliased_pointer} : + !spirv.ptr, Function> +``` +""" +function Variable( + initializer=nothing::Union{Nothing,Value}; + pointer::IR.Type, + storage_class, + location=Location(), +) + _results = IR.Type[pointer,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("storage_class", storage_class),] + !isnothing(initializer) && push!(_operands, initializer) + + return IR.create_operation( + "spirv.Variable", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`VectorExtractDynamic` + +Result Type must be a scalar type. + +Vector must have a type OpTypeVector whose Component Type is Result +Type. + +Index must be a scalar integer. It is interpreted as a 0-based index of +which component of Vector to extract. + +Behavior is undefined if Index\'s value is less than zero or greater than +or equal to the number of components in Vector. + + + +#### Example: + +``` +%2 = spirv.VectorExtractDynamic %0[%1] : vector<8xf32>, i32 +``` +""" +function VectorExtractDynamic( + vector::Value, index::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[vector, index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.VectorExtractDynamic", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`VectorInsertDynamic` + +Result Type must be an OpTypeVector. + +Vector must have the same type as Result Type and is the vector that the +non-written components are copied from. + +Component is the value supplied for the component selected by Index. It +must have the same type as the type of components in Result Type. + +Index must be a scalar integer. It is interpreted as a 0-based index of +which component to modify. + +Behavior is undefined if Index\'s value is less than zero or greater than +or equal to the number of components in Vector. + +#### Example: + +```mlir +%scalar = ... : f32 +%2 = spirv.VectorInsertDynamic %scalar %0[%1] : f32, vector<8xf32>, i32 +``` +""" +function VectorInsertDynamic( + vector::Value, + component::Value, + index::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vector, component, index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "spirv.VectorInsertDynamic", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`VectorShuffle` + +Result Type must be an OpTypeVector. The number of components in Result +Type must be the same as the number of Component operands. + +Vector 1 and Vector 2 must both have vector types, with the same +Component Type as Result Type. They do not have to have the same number +of components as Result Type or with each other. They are logically +concatenated, forming a single vector with Vector 1\'s components +appearing before Vector 2\'s. The components of this logical vector are +logically numbered with a single consecutive set of numbers from 0 to N +- 1, where N is the total number of components. + +Components are these logical numbers (see above), selecting which of the +logically numbered components form the result. Each component is an +unsigned 32-bit integer. They can select the components in any order +and can repeat components. The first component of the result is selected +by the first Component operand, the second component of the result is +selected by the second Component operand, etc. A Component literal may +also be FFFFFFFF, which means the corresponding result component has no +source and is undefined. All Component literals must either be FFFFFFFF +or in [0, N - 1] (inclusive). + +Note: A vector “swizzle” can be done by using the vector for both Vector +operands, or using an OpUndef for one of the Vector operands. + + + +#### Example: + +```mlir +%0 = spirv.VectorShuffle [1: i32, 3: i32, 5: i32] %vector1, %vector2 : + vector<4xf32>, vector<2xf32> -> vector<3xf32> +``` +""" +function VectorShuffle( + vector1::Value, vector2::Value; result::IR.Type, components, location=Location() +) + _results = IR.Type[result,] + _operands = Value[vector1, vector2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("components", components),] + + return IR.create_operation( + "spirv.VectorShuffle", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`VectorTimesScalar` + +Result Type must be a vector of floating-point type. + + The type of Vector must be the same as Result Type. Each component of +Vector is multiplied by Scalar. + +Scalar must have the same type as the Component Type in Result Type. + + + +#### Example: + +```mlir +%0 = spirv.VectorTimesScalar %vector, %scalar : vector<4xf32> +``` +""" +function VectorTimesScalar( + vector::Value, scalar::Value; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[vector, scalar] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.VectorTimesScalar", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mlir_yield` + +This op is a special terminator whose only purpose is to terminate +an `spirv.SpecConstantOperation`\'s enclosed region. It accepts a +single operand produced by the preceeding (and only other) instruction +in its parent block (see SPIRV_SpecConstantOperation for further +details). This op has no corresponding SPIR-V instruction. + +#### Example: + +```mlir +%0 = ... (some op supported by SPIR-V OpSpecConstantOp) +spirv.mlir.yield %0 +``` +""" +function mlir_yield(operand::Value; location=Location()) + _results = IR.Type[] + _operands = Value[operand,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "spirv.mlir.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # spirv diff --git a/src/Dialects/19/Shape.jl b/src/Dialects/19/Shape.jl new file mode 100644 index 00000000..06993eed --- /dev/null +++ b/src/Dialects/19/Shape.jl @@ -0,0 +1,1398 @@ +module shape + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`add` + +Adds two sizes or indices. If either operand is an error it will be +propagated to the result. The operands can be of type `size` or `index`. If +at least one of the operands can hold an error, i.e. if it is of type +`size`, the result must be of type `size`. If error propagation is not +possible because both operands are of type `index` then the result may be +of type `size` or `index`. +""" +function add( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`any` + +This operation takes multiple input shapes or extent tensors and returns +some combination of their dimensions. This can be best seen with examples +below. + +The result is undefined, but still side-effect free, in cases where the +inputs have differing ranks or differ in extents of shared dimensions. + +# Example +```mlir +%s0 = shape.any [2,?], [?,3] // [2,3] +%s1 = shape.any [?,?], [1,2] // [1,2] +``` +""" +function any(inputs::Vector{Value}; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[inputs...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.any", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`assuming_all` + +Used to simplify constraints as any single failing precondition is enough +to prevent execution. + +\"assuming\" operations represent an execution order restriction to the +compiler, information for dependent code to rely on (by assuming), and +nothing else. They should not exist after a program is fully lowered and +ready to execute. + +# Example +```mlir +%w0 = shape.cstr_broadcastable [2,2], [3,1,2] // Passing +%w1 = shape.cstr_broadcastable [2,2], [3,2] // Failure +%w2 = shape.cstr_eq [1,2], [1,2], [1,2] // Passing +%wf = shape.assuming_all %w0, %w1 // Failure +%wt = shape.assuming_all %w0, %w2 // Passing +``` +""" +function assuming_all( + inputs::Vector{Value}; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[inputs...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.assuming_all", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`assuming` + +Executes the region assuming all witnesses are true. + +\"assuming\" operations represent an execution order restriction to the +compiler, information for dependent code to rely on (by assuming), and +nothing else. They should not exist after a program is fully lowered and +ready to execute. +""" +function assuming( + witness::Value; results::Vector{IR.Type}, doRegion::Region, location=Location() +) + _results = IR.Type[results...,] + _operands = Value[witness,] + _owned_regions = Region[doRegion,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.assuming", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`assuming_yield` + +This yield operation represents a return operation within the +`shape.assuming` operation region. The operation takes variable number of +operands and produces no results. The operand number and types must match +the number and types of parent `shape.assuming` results. +""" +function assuming_yield(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.assuming_yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`broadcast` + +Returns the broadcasted shape for input shapes or extent tensors. The rest +of this description is simplified for the 2 input case but can be extended +to more inputs. Both operands can be of type `shape.shape` or +`tensor`. The result is of type `shape.shape` and, if both +operands are tensors, may be of type `tensor`. + +If the two operand shapes are of different rank the smaller one is padded +with 1\'s from the left. The resulting broadcasted shape is then defined as + + result[i] = lhs[i] if lhs[i] == rhs[i] + = lhs[i] if rhs[i] == 1 + = rhs[i] if lhs[i] == 1. + +In case the resulting shape is undefined, i.e. if corresponding extents are +different from each other but none is 1, the result is an error shape. +Likewise error values are propagated if any of the operands holds an error +value. If the result type is an extent tensor (and can therefore not hold +the error value) the behavior may be undefined. The optional string +attribute can be used to describe the error case. +""" +function broadcast( + shapes::Vector{Value}; result::IR.Type, error=nothing, location=Location() +) + _results = IR.Type[result,] + _operands = Value[shapes...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(error) && push!(_attributes, namedattribute("error", error)) + + return IR.create_operation( + "shape.broadcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`concat` + +Creates a shape whose dimensions consist of first the dimensions from `lhs` +followed by the dimensions of `rhs`. + +# Example +concat([2,3], [4,5]) -> [2,3,4,5] +concat([], []) -> [] +concat([], [4,5,6]) -> [4,5,6] +""" +function concat(lhs::Value, rhs::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.concat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`const_shape` + +Creates a constant shape or extent tensor. The individual extents are given +as the `shape` attribute. The number of these values equals the shape\'s +rank. + +```mlir +%0 = shape.const_shape [] : !shape.shape +%1 = shape.const_shape [1, 2, 3] : !shape.shape +%2 = shape.const_shape [4, 5, 6] : tensor<3xindex> +``` +""" +function const_shape(; result=nothing::Union{Nothing,IR.Type}, shape, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("shape", shape),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.const_shape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`const_size` + +Creates a `shape.size` type representing the constant size given by `value`. + +```mlir +%x = shape.const_size 10 +``` +""" +function const_size(; result=nothing::Union{Nothing,IR.Type}, value, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.const_size", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`const_witness` + +This operation represents a statically known witness result. This can be +often used to canonicalize/fold constraint and assuming code that will always +pass. + +```mlir +%0 = shape.const_shape [1,2,3] +%1 = shape.const_shape [1,2,3] +%w0 = shape.cstr_eq(%0, %1) // Can be folded to \"const_witness true\" +%w1 = shape.const_witness true +%w2 = shape.assuming_all(%w0, %w2) // Can be folded to \"const_witness true\" +``` +""" +function const_witness(; + result=nothing::Union{Nothing,IR.Type}, passing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("passing", passing),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.const_witness", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cstr_broadcastable` + +Given input shapes or extent tensors, return a witness specifying if they +are broadcastable. This broadcastable follows the same logic as what +shape.broadcast documents. + +\"cstr\" operations represent runtime assertions. + +# Example +```mlir +%w0 = shape.cstr_broadcastable [2,2], [3,1,2] // Passing +%w1 = shape.cstr_broadcastable [2,2], [3,2] // Failure +``` +""" +function cstr_broadcastable( + shapes::Vector{Value}; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[shapes...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.cstr_broadcastable", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cstr_eq` + +Given 1 or more input shapes, determine if all shapes are the exact same. + +\"cstr\" operations represent runtime assertions. + +# Example +```mlir +%w0 = shape.cstr_eq [1,2], [1,2], [1,2] // Passing +%w1 = shape.cstr_eq [2,2], [1,2] // Failure +``` +""" +function cstr_eq( + shapes::Vector{Value}; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[shapes...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.cstr_eq", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`cstr_require` + +Represents a runtime assertion that an i1 is true. It returns a +!shape.witness to order this assertion. + +For simplicity, prefer using other cstr_* ops if they are available for a +given constraint. + +# Example +```mlir +%bool = ... +%w0 = shape.cstr_require %bool, \"msg\" // Passing if `%bool` is true. +``` + +Since this op can be used to express many different possible assertions +(depending on whatever computation calculated `pred`), the `msg` +should clarify the nature of the assertion for users. +""" +function cstr_require( + pred::Value; result=nothing::Union{Nothing,IR.Type}, msg, location=Location() +) + _results = IR.Type[] + _operands = Value[pred,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("msg", msg),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.cstr_require", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`debug_print` + +Prints the input dim or shape and passes through input. + +Note: This is intended for testing and debugging only. +""" +function debug_print(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.debug_print", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`dim` + +Gets the extent indexed by `dim` from the shape of the `value` operand. If +the index is error or out-of-bound then it returns an invalid size if the +return type carries error information else the behavior is undefined. + +This is a convenience op that performs the equivalent of getting the extent +of a shape (e.g., `dim(x, i) == get_extent(shape_of(x), i)`). +""" +function dim( + value::Value, index::Value; extent=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[value, index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(extent) && push!(_results, extent) + + return IR.create_operation( + "shape.dim", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`div` + +Divides two sizes or indices. If either operand is an error it will be +propagated to the result. The operands can be of type `size` or `index`. +If at least one of the operands can hold an error, i.e. if it is of type +`size`, the result must be of type `size`. If error propagation is not +possible because both operands are of type `index` then the result may be +of type `size` or `index`. If both operands and result are of type +`index`, their runtime values could be negative. The result is rounded +toward negative infinity, i.e. floor(lhs / rhs), such that + + div(lhs, rhs) * rhs + mod(lhs, rhs) = lhs + +always holds. If any of the values is of type `size`, the behavior for +negative value is undefined. +""" +function div( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.div", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`from_extent_tensor` + +Creates a shape from a 1D integral tensor of extents. The rank of the +resulting shape equals the number of elements in the tensor, and the +extents match the values of the elements. +""" +function from_extent_tensor( + input::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.from_extent_tensor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`from_extents` + +Creates a shape from multiple SSA values representing the extents of +the shape. + +```mlir +// Rank 2 shape. +%s0 = shape.from_extents %a, %b +// Rank 0 shape. +%s1 = shape.from_extents +``` +""" +function from_extents( + extents::Vector{Value}; shape=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[extents...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(shape) && push!(_results, shape) + + return IR.create_operation( + "shape.from_extents", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`func` + +An operation with a name containing a single `SSACFG` region which +represents a shape transfer function or helper function for shape transfer +function. +""" +function func(; + sym_name, + function_type, + arg_attrs=nothing, + res_attrs=nothing, + sym_visibility=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("function_type", function_type) + ] + !isnothing(arg_attrs) && push!(_attributes, namedattribute("arg_attrs", arg_attrs)) + !isnothing(res_attrs) && push!(_attributes, namedattribute("res_attrs", res_attrs)) + !isnothing(sym_visibility) && + push!(_attributes, namedattribute("sym_visibility", sym_visibility)) + + return IR.create_operation( + "shape.func", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`function_library` + +Represents a list of shape functions and the ops whose shape transfer +functions they represent. + +# Example + +```mlir +shape.function_library { + func @same_result_shape(%arg: !shape.value_shape) -> !shape.shape { + %0 = shape_of %arg : !shape.value_shape -> !shape.shape + return %0 : !shape.shape + } +} mapping { + std.atan = @same_result_shape +} +``` +""" +function function_library(; + sym_name, sym_visibility=nothing, mapping, body::Region, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("mapping", mapping) + ] + !isnothing(sym_visibility) && + push!(_attributes, namedattribute("sym_visibility", sym_visibility)) + + return IR.create_operation( + "shape.function_library", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_extent` + +Gets the extent indexed by `dim` from the `shape` operand. If the shape is +an error then it returns an invalid size. +""" +function get_extent( + shape::Value, dim::Value; extent=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[shape, dim] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(extent) && push!(_results, extent) + + return IR.create_operation( + "shape.get_extent", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`index_to_size` + +Converts a standard index to a `shape.size`. This operation and its +inverse, `size_to_index`, facilitate index conversion between the standard +and the shape dialect. + +The behavior is undefined for negative indices. +""" +function index_to_size( + arg::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.index_to_size", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`is_broadcastable` + +Given multiple input shapes or extent tensors, return a predicate +specifying if they are broadcastable. This broadcastable follows the same +logic as what shape.broadcast documents. + +Concretely, shape.is_broadcastable returning true implies that +shape.broadcast will not give an error, and shape.cstr_broadcastable will +not result in an assertion failure. Similarly, false implies an error or +assertion failure. + +# Example +```mlir +%true = shape.is_broadcastable [2,2], [3,1,2] +%false = shape.is_broadcastable [2,2], [3,2] +``` +""" +function is_broadcastable( + shapes::Vector{Value}; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[shapes...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.is_broadcastable", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`max` + +Computes the elementwise maximum of two sizes or shapes with equal ranks. +If either operand is an error, then an error will be propagated to the +result. If the input types mismatch or the ranks do not match, then the +result is an error. +""" +function max( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.max", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`meet` + +An operation that computes the least general shape or dim of input operands. +This effectively asserts that corresponding static dimensions are equal. +The behavior is to match each element of the shape/size and propagate the +most restrictive information, returning an invalid shape if there are +contradictory requirements. E.g., using pseudo code + +``` +shape.meet([*], [*]) -> [*] +shape.meet([*], [1, ?]) -> [1, ?] +shape.meet([1, 2], [1, ?]) -> [1, 2] +shape.meet([*], [1, 2]) -> [1, 2] +shape.meet([], []) -> [] +shape.meet([], [*]) -> [] +shape.meet([], [?, ?]) -> [invalid] +shape.meet([1, ?], [2, ?, ?]) -> [invalid] +``` + +`shape.meet` also allows specifying an optional error string, that may be +used to return an error to the user upon mismatch of dimensions. + +```mlir +%c = shape.meet %a, %b, error=\"\" : !shape.shape, !shape.shape -> !shape.shape +``` +""" +function meet( + arg0::Value, + arg1::Value; + result=nothing::Union{Nothing,IR.Type}, + error=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[arg0, arg1] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(error) && push!(_attributes, namedattribute("error", error)) + + return IR.create_operation( + "shape.meet", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`min` + +Computes the elementwise minimum of two sizes or shapes with equal ranks. +If either operand is an error, then an error will be propagated to the +result. If the input types mismatch or the ranks do not match, then the +result is an error. +""" +function min( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.min", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`mul` + +Multiplies two sizes or indices. If either operand is an error it will be +propagated to the result. The operands can be of type `size` or `index`. If +at least one of the operands can hold an error, i.e. if it is of type +`size`, the result must be of type `size`. If error propagation is not +possible because both operands are of type `index` then the result may be +of type `size` or `index`. +""" +function mul( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`num_elements` + +Returns the number of elements for a given shape which is the product of +its extents. If the argument is of type `shape` then the result will be of +type `size` and potential errors will be propagated. Otherwise, if the +argument is and extent tensor `tensor` then the result will be of +type `index`. +""" +function num_elements( + shape::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[shape,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.num_elements", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`rank` + +Returns the rank of the shape or extent tensor, i.e. the number of extents. +""" +function rank(shape::Value; rank=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[shape,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(rank) && push!(_results, rank) + + return IR.create_operation( + "shape.rank", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`reduce` + +An operation that takes as input a shape or extent tensor, and a number of +initial values. This operation has a region that is applied repeatedly for +every extent of the input. Starting with the initial values, the individual +extents are then aggregated as defined by the associated region. + +Conceptually this op performs the following reduction: + +``` +res[] = init; +for (int i = 0, i < shape.rank(); i++) { + res = reduce(i, shape[i], res[0], ..., res[n]); +} +``` + +Where `reduce` represents the region attached and the result of the reduce +op is the last computed output of the reduce region. As an example, the +number of elements can be computed as follows: + +```mlir +func.func @reduce(%shape : !shape.shape, %init : !shape.size) -> + !shape.size { + %num_elements = shape.reduce(%shape, %init) -> !shape.size { + ^bb0(%index: index, %dim: !shape.size, %acc: !shape.size): + %updated_acc = \"shape.mul\"(%acc, %dim) : + (!shape.size, !shape.size) -> !shape.size + shape.yield %updated_acc : !shape.size + } + return %num_elements : !shape.size +} +``` +""" +function reduce( + shape::Value, + initVals::Vector{Value}; + result::Vector{IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[result...,] + _operands = Value[shape, initVals...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.reduce", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`return_` + +The `shape.return` operation represents a return operation within a +function. The operation takes variable number of operands and produces no +results. +""" +function return_(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.return", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`shape_eq` + +Takes one or more shape or extent tensor operands and determines whether +they are equal. When extent tensors are compared to shapes they are +regarded as their equivalent non-error shapes. Error shapes can be tested +for equality like any other shape value, meaning that the error value is +equal to itself. +""" +function shape_eq( + shapes::Vector{Value}; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[shapes...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.shape_eq", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`shape_of` + +The operation takes a value or a shaped operand as an argument and it +returns a shape or extent tensor. +""" +function shape_of(arg::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.shape_of", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`size_to_index` + +Converts a `shape.size` to a standard index. This operation and its +inverse, `index_to_size`, facilitate index conversion between the standard +and the shape dialect. The behavior is undefined for unknown and invalid +arguments. +""" +function size_to_index( + arg::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.size_to_index", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`split_at` + +Splits a shape at a given dimension `index`, returning two shapes. If +`index` is negative, it is treated as indexing from the back of the shape. +This negative-handling behavior is important when handling unranked shapes, +where the positive index is not necessarily knowable due to a dynamic +number of leading dimensions. If the result is in extent tensor form out of +bounds indices result in undefined behavior. + +Examples: +- split_at([4,5,6], index=0) -> [], [4,5,6] +- split_at([4,5,6], index=1) -> [4], [5,6] +- split_at([4,5,6], index=2) -> [4,5], [6] +- split_at([4,5,6], index=3) -> [4,5,6], [] +- split_at([4,5,6], index=4) -> error +- split_at([4,5,6], index=-1) -> [4,5], [6] +- split_at([4,5,6], index=-2) -> [4], [5,6] +- split_at([4,5,6], index=-3) -> [], [4,5,6] +- split_at([4,5,6], index=-4) -> error + +Requires: +- `index` is in the range [-rank(operand),rank(operand)] +""" +function split_at( + operand::Value, index::Value; head::IR.Type, tail::IR.Type, location=Location() +) + _results = IR.Type[head, tail] + _operands = Value[operand, index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.split_at", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`to_extent_tensor` + +Converts a shape to a 1D integral tensor of extents. The number of elements +in the tensor equals the rank of the shape, and the elements equal the +extents of the shape. + +If the shape represents an error, this op\'s behavior is undefined. +""" +function to_extent_tensor(input::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.to_extent_tensor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`value_as_shape` + +The operations takes a ValueShape and returns a Shape corresponding to the +value. If the input value cannot be shape (e.g., not a 1D tensor of +integral value representing sizes) then this propagages the error shape. +E.g., + +```mlir +// The following +%0 = arith.constant dense<[1,2]> : tensor<2xi32> +%shape = shape.value_as_shape %0 : tensor<2xi32> -> !shape.shape +// is equivalent to +%shape\' = shape.const_shape [1, 2] : !shape.shape +``` + +This operation is the complement of `shape_of` wrt ValueShape values. +""" +function value_as_shape(arg::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.value_as_shape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`value_of` + +The operation takes !shape.value_shape, a.k.a. (value, shape) tuple as an +argument, and returns its value. The behavior is undefined for unknown and +invalid arguments. +""" +function value_of(arg::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[arg,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.value_of", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`with_shape` + +Returns ValueShape with the shape updated to match the shape operand. That +is a new ValueShape tuple is created with value equal to `operand`\'s +value and shape equal to `shape`. If the ValueShape and given `shape` are +non-conformant, then the returned ValueShape will represent an error of +this mismatch. Similarly if either inputs are in an error state, then an +error is propagated. + +Usage: + %0 = shape.with_shape %1, %2 : tensor<...>, !shape.shape + +This is used, for example, where one combines shape function calculations +and/or call one shape function from another. E.g., + +```mlir +func.func @shape_foobah(%a: !shape.value_shape, + %b: !shape.value_shape, + %c: !shape.value_shape) -> !shape.shape { + %0 = call @shape_foo(%a, %b) : + (!shape.value_shape, !shape.value_shape) -> !shape.shape + %1 = shape.with_shape %b, %0 : !shape.value_shape, !shape.shape + %2 = call @shape_bah(%c, %1) : + (!shape.value_shape, !shape.value_shape) -> !shape.shape + return %2 : !shape.shape +} +``` + +This op need not be a refinement of the shape. In non-error cases the input +ValueShape\'s value and shape are conformant and so too for the output, but +the result may be less specified than `operand`\'s shape as `shape` is +merely used to construct the new ValueShape. If join behavior is desired +then a join op should be used. +""" +function with_shape( + operand::Value, + shape::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand, shape] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "shape.with_shape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`yield` + +""" +function yield(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "shape.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # shape diff --git a/src/Dialects/19/SparseTensor.jl b/src/Dialects/19/SparseTensor.jl new file mode 100644 index 00000000..6cf56039 --- /dev/null +++ b/src/Dialects/19/SparseTensor.jl @@ -0,0 +1,1958 @@ +module sparse_tensor + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`extract_iteration_space` + +Extracts a `!sparse_tensor.iter_space` from a sparse tensor between +certain (consecutive) levels. For sparse levels, it is usually done by +loading a postion range from the underlying sparse tensor storage. +E.g., for a compressed level, the iteration space is extracted by +[pos[i], pos[i+1]) supposing the the parent iterator points at `i`. + +`tensor`: the input sparse tensor that defines the iteration space. +`parentIter`: the iterator for the previous level, at which the iteration space +at the current levels will be extracted. +`loLvl`, `hiLvl`: the level range between [loLvl, hiLvl) in the input tensor that +the returned iteration space covers. `hiLvl - loLvl` defines the dimension of the +iteration space. + +The type of returned the value is must be +`!sparse_tensor.iter_space<#INPUT_ENCODING, lvls = \$loLvl to \$hiLvl>`. +The returned iteration space can then be iterated over by +`sparse_tensor.iterate` operations to visit every stored element +(usually nonzeros) in the input sparse tensor. + +# Example +```mlir +// Extracts a 1-D iteration space from a COO tensor at level 1. +%space = sparse_tensor.iteration.extract_space %sp at %it1 lvls = 1 + : tensor<4x8xf32, #COO>, !sparse_tensor.iterator<#COO, lvls = 0> + ->!sparse_tensor.iter_space<#COO, lvls = 1> +``` +""" +function extract_iteration_space( + tensor::Value, + parentIter=nothing::Union{Nothing,Value}; + extractedSpace=nothing::Union{Nothing,IR.Type}, + loLvl, + hiLvl, + location=Location(), +) + _results = IR.Type[] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("loLvl", loLvl), namedattribute("hiLvl", hiLvl) + ] + !isnothing(parentIter) && push!(_operands, parentIter) + !isnothing(extractedSpace) && push!(_results, extractedSpace) + + return IR.create_operation( + "sparse_tensor.extract_iteration_space", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`iterate` + +The `sparse_tensor.iterate` operation represents a loop (nest) over +the provided iteration space extracted from a specific sparse tensor. +The operation defines an SSA value for a sparse iterator that points +to the current stored element in the sparse tensor and SSA values +for coordinates of the stored element. The coordinates are always +converted to `index` type despite of the underlying sparse tensor +storage. When coordinates are not used, the SSA values can be skipped +by `_` symbols, which usually leads to simpler generated code after +sparsification. For example: + +```mlir +// The coordinate for level 0 is not used when iterating over a 2-D +// iteration space. +%sparse_tensor.iterate %iterator in %space at(_, %crd_1) + : !sparse_tensor.iter_space<#CSR, lvls = 0 to 2> +``` + +`sparse_tensor.iterate` can also operate on loop-carried variables. +It returns the final values after loop termination. +The initial values of the variables are passed as additional SSA operands +to the iterator SSA value and used coordinate SSA values mentioned +above. The operation region has an argument for the iterator, variadic +arguments for specified (used) coordiates and followed by one argument +for each loop-carried variable, representing the value of the variable +at the current iteration. +The body region must contain exactly one block that terminates with +`sparse_tensor.yield`. + +The results of an `sparse_tensor.iterate` hold the final values after +the last iteration. If the `sparse_tensor.iterate` defines any values, +a yield must be explicitly present. +The number and types of the `sparse_tensor.iterate` results must match +the initial values in the iter_args binding and the yield operands. + + +A nested `sparse_tensor.iterate` example that prints all the coordinates +stored in the sparse input: + +```mlir +func.func @nested_iterate(%sp : tensor<4x8xf32, #COO>) { + // Iterates over the first level of %sp + %l1 = sparse_tensor.extract_iteration_space %sp lvls = 0 + : tensor<4x8xf32, #COO> -> !sparse_tensor.iter_space<#COO, lvls = 0 to 1> + %r1 = sparse_tensor.iterate %it1 in %l1 at (%coord0) + : !sparse_tensor.iter_space<#COO, lvls = 0 to 1> { + // Iterates over the second level of %sp + %l2 = sparse_tensor.extract_iteration_space %sp at %it1 lvls = 1 + : tensor<4x8xf32, #COO>, !sparse_tensor.iterator<#COO, lvls = 0 to 1> + -> !sparse_tensor.iter_space<#COO, lvls = 1 to 2> + %r2 = sparse_tensor.iterate %it2 in %l2 at (coord1) + : !sparse_tensor.iter_space<#COO, lvls = 1 to 2> { + vector.print %coord0 : index + vector.print %coord1 : index + } + } +} + +``` +""" +function iterate( + iterSpace::Value, + initArgs::Vector{Value}; + results::Vector{IR.Type}, + crdUsedLvls, + region::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[iterSpace, initArgs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("crdUsedLvls", crdUsedLvls),] + + return IR.create_operation( + "sparse_tensor.iterate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`assemble` + +Assembles the per-level position and coordinate arrays together with +the values arrays into a sparse tensor. The order and types of the +provided levels must be consistent with the actual storage layout of +the returned sparse tensor described below. + +- `levels: [tensor, ...]` + supplies the sparse tensor position and coordinate arrays + of the sparse tensor for the corresponding level as specifed by + `sparse_tensor::StorageLayout`. +- `values : tensor` + supplies the values array for the stored elements in the sparse tensor. + +This operation can be used to assemble a sparse tensor from an +external source; e.g., by passing numpy arrays from Python. It +is the user\'s responsibility to provide input that can be correctly +interpreted by the sparsifier, which does not perform any sanity +test to verify data integrity. + +# Example + +```mlir +%pos = arith.constant dense<[0, 3]> : tensor<2xindex> +%index = arith.constant dense<[[0,0], [1,2], [1,3]]> : tensor<3x2xindex> +%values = arith.constant dense<[ 1.1, 2.2, 3.3 ]> : tensor<3xf64> +%s = sparse_tensor.assemble (%pos, %index), %values + : (tensor<2xindex>, tensor<3x2xindex>), tensor<3xf64> to tensor<3x4xf64, #COO> +// yields COO format |1.1, 0.0, 0.0, 0.0| +// of 3x4 matrix |0.0, 0.0, 2.2, 3.3| +// |0.0, 0.0, 0.0, 0.0| +``` +""" +function assemble( + levels::Vector{Value}, values::Value; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[levels..., values] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "sparse_tensor.assemble", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`binary` + +Defines a computation within a `linalg.generic` operation that takes two +operands and executes one of the regions depending on whether both operands +or either operand is nonzero (i.e. stored explicitly in the sparse storage +format). + +Three regions are defined for the operation and must appear in this order: +- overlap (elements present in both sparse tensors) +- left (elements only present in the left sparse tensor) +- right (element only present in the right sparse tensor) + +Each region contains a single block describing the computation and result. +Every non-empty block must end with a sparse_tensor.yield and the return +type must match the type of `output`. The primary region\'s block has two +arguments, while the left and right region\'s block has only one argument. + +A region may also be declared empty (i.e. `left={}`), indicating that the +region does not contribute to the output. For example, setting both +`left={}` and `right={}` is equivalent to the intersection of the two +inputs as only the overlap region will contribute values to the output. + +As a convenience, there is also a special token `identity` which can be +used in place of the left or right region. This token indicates that +the return value is the input value (i.e. func(%x) => return %x). +As a practical example, setting `left=identity` and `right=identity` +would be equivalent to a union operation where non-overlapping values +in the inputs are copied to the output unchanged. + +Due to the possibility of empty regions, i.e. lack of a value for certain +cases, the result of this operation may only feed directly into the output +of the `linalg.generic` operation or into into a custom reduction +`sparse_tensor.reduce` operation that follows in the same region. + +Example of isEqual applied to intersecting elements only: + +```mlir +%C = tensor.empty(...) +%0 = linalg.generic #trait + ins(%A: tensor, + %B: tensor) + outs(%C: tensor) { + ^bb0(%a: f64, %b: f64, %c: i8) : + %result = sparse_tensor.binary %a, %b : f64, f64 to i8 + overlap={ + ^bb0(%arg0: f64, %arg1: f64): + %cmp = arith.cmpf \"oeq\", %arg0, %arg1 : f64 + %ret_i8 = arith.extui %cmp : i1 to i8 + sparse_tensor.yield %ret_i8 : i8 + } + left={} + right={} + linalg.yield %result : i8 +} -> tensor +``` + +Example of A+B in upper triangle, A-B in lower triangle: + +```mlir +%C = tensor.empty(...) +%1 = linalg.generic #trait + ins(%A: tensor, %B: tensor + outs(%C: tensor { + ^bb0(%a: f64, %b: f64, %c: f64) : + %row = linalg.index 0 : index + %col = linalg.index 1 : index + %result = sparse_tensor.binary %a, %b : f64, f64 to f64 + overlap={ + ^bb0(%x: f64, %y: f64): + %cmp = arith.cmpi \"uge\", %col, %row : index + %upperTriangleResult = arith.addf %x, %y : f64 + %lowerTriangleResult = arith.subf %x, %y : f64 + %ret = arith.select %cmp, %upperTriangleResult, %lowerTriangleResult : f64 + sparse_tensor.yield %ret : f64 + } + left=identity + right={ + ^bb0(%y: f64): + %cmp = arith.cmpi \"uge\", %col, %row : index + %lowerTriangleResult = arith.negf %y : f64 + %ret = arith.select %cmp, %y, %lowerTriangleResult : f64 + sparse_tensor.yield %ret : f64 + } + linalg.yield %result : f64 +} -> tensor +``` + +Example of set difference. Returns a copy of A where its sparse structure +is *not* overlapped by B. The element type of B can be different than A +because we never use its values, only its sparse structure: + +```mlir +%C = tensor.empty(...) +%2 = linalg.generic #trait + ins(%A: tensor, %B: tensor + outs(%C: tensor { + ^bb0(%a: f64, %b: i32, %c: f64) : + %result = sparse_tensor.binary %a, %b : f64, i32 to f64 + overlap={} + left=identity + right={} + linalg.yield %result : f64 +} -> tensor +``` +""" +function binary( + x::Value, + y::Value; + output::IR.Type, + left_identity=nothing, + right_identity=nothing, + overlapRegion::Region, + leftRegion::Region, + rightRegion::Region, + location=Location(), +) + _results = IR.Type[output,] + _operands = Value[x, y] + _owned_regions = Region[overlapRegion, leftRegion, rightRegion] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(left_identity) && + push!(_attributes, namedattribute("left_identity", left_identity)) + !isnothing(right_identity) && + push!(_attributes, namedattribute("right_identity", right_identity)) + + return IR.create_operation( + "sparse_tensor.binary", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`compress` + +Finishes a single access pattern expansion by moving inserted elements +into the sparse storage scheme of the given tensor with the given +level-coordinates. The arity of `lvlCoords` is one less than the +level-rank of the tensor, with the coordinate of the innermost +level defined through the `added` array. The `values` and `filled` +arrays are reset in a *sparse* fashion by only iterating over set +elements through an indirection using the `added` array, so that +the operations are kept proportional to the number of nonzeros. +See the `sparse_tensor.expand` operation for more details. + +Note that this operation is \"impure\" in the sense that even though +the result is modeled through an SSA value, the insertion is eventually +done \"in place\", and referencing the old SSA value is undefined behavior. + +# Example + +```mlir +%result = sparse_tensor.compress %values, %filled, %added, %count into %tensor[%i] + : memref, memref, memref, tensor<4x4xf64, #CSR> +``` +""" +function compress( + values::Value, + filled::Value, + added::Value, + count::Value, + tensor::Value, + lvlCoords::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[values, filled, added, count, tensor, lvlCoords...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "sparse_tensor.compress", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`concatenate` + +Concatenates a list input tensors and the output tensor with the same +dimension-rank. The concatenation happens on the specified `dimension` +(0 <= dimension < dimRank). The resulting `dimension` size is the +sum of all the input sizes for that dimension, while all the other +dimensions should have the same size in the input and output tensors. + +Only statically-sized input tensors are accepted, while the output tensor +can be dynamically-sized. + +# Example + +```mlir +%0 = sparse_tensor.concatenate %1, %2 { dimension = 0 : index } + : tensor<64x64xf64, #CSR>, tensor<64x64xf64, #CSR> to tensor<128x64xf64, #CSR> +``` +""" +function concatenate(inputs::Vector{Value}; result::IR.Type, dimension, location=Location()) + _results = IR.Type[result,] + _operands = Value[inputs...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dimension", dimension),] + + return IR.create_operation( + "sparse_tensor.concatenate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`convert` + +Converts one sparse or dense tensor type to another tensor type. The rank +of the source and destination types must match exactly, and the dimension +sizes must either match exactly or relax from a static to a dynamic size. +The sparse encoding of the two types can obviously be completely different. +The name `convert` was preferred over `cast`, since the operation may incur +a non-trivial cost. + +When converting between two different sparse tensor types, only explicitly +stored values are moved from one underlying sparse storage format to +the other. When converting from an unannotated dense tensor type to a +sparse tensor type, an explicit test for nonzero values is used. When +converting to an unannotated dense tensor type, implicit zeroes in the +sparse storage format are made explicit. Note that the conversions can have +non-trivial costs associated with them, since they may involve elaborate +data structure transformations. Also, conversions from sparse tensor types +into dense tensor types may be infeasible in terms of storage requirements. + +Trivial dense-to-dense convert will be removed by canonicalization while +trivial sparse-to-sparse convert will be removed by the sparse codegen. This +is because we use trivial sparse-to-sparse convert to tell bufferization +that the sparse codegen will expand the tensor buffer into sparse tensor +storage. + +Examples: + +```mlir +%0 = sparse_tensor.convert %a : tensor<32x32xf32> to tensor<32x32xf32, #CSR> +%1 = sparse_tensor.convert %a : tensor<32x32xf32> to tensor +%2 = sparse_tensor.convert %b : tensor<8x8xi32, #CSC> to tensor<8x8xi32, #CSR> +%3 = sparse_tensor.convert %c : tensor<4x8xf64, #CSR> to tensor<4x?xf64, #CSC> + +// The following conversion is not allowed (since it would require a +// runtime assertion that the source\'s dimension size is actually 100). +%4 = sparse_tensor.convert %d : tensor to tensor<100xf64, #SV> +``` +""" +function convert(source::Value; dest::IR.Type, location=Location()) + _results = IR.Type[dest,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "sparse_tensor.convert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`crd_translate` + +Performs coordinate translation between level and dimension coordinate space according +to the affine maps defined by \$encoder. + +# Example + +```mlir +%l0, %l1, %l2, %l3 = sparse_tensor.crd_translate dim_to_lvl [%d0, %d1] as #BSR + : index, index, index, index +``` +""" +function crd_translate( + in_crds::Vector{Value}; + out_crds::Vector{IR.Type}, + direction, + encoder, + location=Location(), +) + _results = IR.Type[out_crds...,] + _operands = Value[in_crds...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("direction", direction), namedattribute("encoder", encoder) + ] + + return IR.create_operation( + "sparse_tensor.crd_translate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`disassemble` + +The disassemble operation is the inverse of `sparse_tensor::assemble`. +It copies the per-level position and coordinate arrays together with +the values array of the given sparse tensor into the user-supplied buffers +along with the actual length of the memory used in each returned buffer. + +This operation can be used for returning a disassembled MLIR sparse tensor; +e.g., copying the sparse tensor contents into pre-allocated numpy arrays +back to Python. It is the user\'s responsibility to allocate large enough +buffers of the appropriate types to hold the sparse tensor contents. +The sparsifier simply copies all fields of the sparse tensor into the +user-supplied buffers without any sanity test to verify data integrity. + +# Example + +```mlir +// input COO format |1.1, 0.0, 0.0, 0.0| +// of 3x4 matrix |0.0, 0.0, 2.2, 3.3| +// |0.0, 0.0, 0.0, 0.0| +%p, %c, %v, %p_len, %c_len, %v_len = + sparse_tensor.disassemble %s : tensor<3x4xf64, #COO> + out_lvls(%op, %oi : tensor<2xindex>, tensor<3x2xindex>) + out_vals(%od : tensor<3xf64>) -> + (tensor<2xindex>, tensor<3x2xindex>), tensor<3xf64>, (index, index), index +// %p = arith.constant dense<[ 0, 3 ]> : tensor<2xindex> +// %c = arith.constant dense<[[0,0], [1,2], [1,3]]> : tensor<3x2xindex> +// %v = arith.constant dense<[ 1.1, 2.2, 3.3 ]> : tensor<3xf64> +// %p_len = 2 +// %c_len = 6 (3x2) +// %v_len = 3 +``` +""" +function disassemble( + tensor::Value, + out_levels::Vector{Value}, + out_values::Value; + ret_levels::Vector{IR.Type}, + ret_values::IR.Type, + lvl_lens::Vector{IR.Type}, + val_len::IR.Type, + location=Location(), +) + _results = IR.Type[ret_levels..., ret_values, lvl_lens..., val_len] + _operands = Value[tensor, out_levels..., out_values] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "sparse_tensor.disassemble", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`expand` + +Performs an access pattern expansion for the innermost levels of the +given tensor. This operation is useful to implement kernels in which a +sparse tensor appears as output. This technique is known under several +different names and using several alternative implementations, +for example, phase counter [Gustavson72], expanded or switch array +[Pissanetzky84], in phase scan [Duff90], access pattern expansion [Bik96], +and workspaces [Kjolstad19]. + +The `values` and `filled` arrays must have lengths equal to the +level-size of the innermost level (i.e., as if the innermost level +were *dense*). The `added` array and `count` are used to store new +level-coordinates when a false value is encountered in the `filled` +array. All arrays should be allocated before the loop (possibly even +shared between loops in a future optimization) so that their *dense* +initialization can be amortized over many iterations. Setting and +resetting the dense arrays in the loop nest itself is kept *sparse* +by only iterating over set elements through an indirection using +the added array, so that the operations are kept proportional to +the number of nonzeros. + +Note that this operation is \"impure\" in the sense that even though the +results are modeled through SSA values, the operation relies on a proper +side-effecting context that sets and resets the expanded arrays. + +# Example + +```mlir +%values, %filled, %added, %count = sparse_tensor.expand %tensor + : tensor<4x4xf64, #CSR> to memref, memref, memref +``` +""" +function expand( + tensor::Value; + values::IR.Type, + filled::IR.Type, + added::IR.Type, + count::IR.Type, + location=Location(), +) + _results = IR.Type[values, filled, added, count] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "sparse_tensor.expand", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`foreach` + +Iterates over stored elements in a tensor (which are typically, but not always, +non-zero for sparse tensors) and executes the block. + +`tensor`: the input tensor to iterate over. +`initArgs`: the initial loop argument to carry and update during each iteration. +`order`: an optional permutation affine map that specifies the order in which +the dimensions are visited (e.g., row first or column first). This is only +applicable when the input tensor is a non-annotated dense tensor. + +For an input tensor with dim-rank `n`, the block must take `n + 1` +arguments (plus additional loop-carried variables as described below). +The first `n` arguments provide the dimension-coordinates of the element +being visited, and must all have `index` type. The `(n+1)`-th argument +provides the element\'s value, and must have the tensor\'s element type. + +`sparse_tensor.foreach` can also operate on loop-carried variables and returns +the final values after loop termination. The initial values of the variables are +passed as additional SSA operands to the \"sparse_tensor.foreach\" following the n + 1 +SSA values mentioned above (n coordinates and 1 value). + +The region must terminate with a \"sparse_tensor.yield\" that passes the current +values of all loop-carried variables to the next iteration, or to the +result, if at the last iteration. The number and static types of loop-carried +variables may not change with iterations. + +For example: +```mlir +%c0 = arith.constant 0 : i32 +%ret = sparse_tensor.foreach in %0 init(%c0): tensor, i32 -> i32 do { + ^bb0(%arg1: index, %arg2: index, %arg3: i32, %iter: i32): + %sum = arith.add %iter, %arg3 + sparse_tensor.yield %sum +} +``` + +It is important to note that the generated loop iterates over +elements in their storage order. However, regardless of the +storage scheme used by the tensor, the block is always given +the dimension-coordinates. + +For example: +```mlir +#COL_MAJOR = #sparse_tensor.encoding<{ + map = (d0, d1) -> (d1 : compressed, d0 : compressed) +}> + +// foreach on a column-major sparse tensor +sparse_tensor.foreach in %0 : tensor<2x3xf64, #COL_MAJOR> do { + ^bb0(%row: index, %col: index, %arg3: f64): + // [%row, %col] -> [0, 0], [1, 0], [2, 0], [0, 1], [1, 1], [2, 1] +} + +#ROW_MAJOR = #sparse_tensor.encoding<{ + map = (d0, d1) -> (d0 : compressed, d1 : compressed) +}> + +// foreach on a row-major sparse tensor +sparse_tensor.foreach in %0 : tensor<2x3xf64, #ROW_MAJOR> do { + ^bb0(%row: index, %col: index, %arg3: f64): + // [%row, %col] -> [0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1] +} + +// foreach on a row-major dense tensor but visit column first +sparse_tensor.foreach in %0 {order=affine_map<(i,j)->(j,i)>}: tensor<2x3xf64> do { + ^bb0(%row: index, %col: index, %arg3: f64): + // [%row, %col] -> [0, 0], [1, 0], [2, 0], [0, 1], [1, 1], [2, 1] +} + +``` +""" +function foreach( + tensor::Value, + initArgs::Vector{Value}; + results::Vector{IR.Type}, + order=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[tensor, initArgs...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(order) && push!(_attributes, namedattribute("order", order)) + + return IR.create_operation( + "sparse_tensor.foreach", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`storage_specifier_get` + +Returns the requested field of the given storage_specifier. + +Example of querying the size of the coordinates array for level 0: + +```mlir +%0 = sparse_tensor.storage_specifier.get %arg0 crd_mem_sz at 0 + : !sparse_tensor.storage_specifier<#COO> +``` +""" +function storage_specifier_get( + specifier::Value; + result=nothing::Union{Nothing,IR.Type}, + specifierKind, + level=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[specifier,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("specifierKind", specifierKind),] + !isnothing(result) && push!(_results, result) + !isnothing(level) && push!(_attributes, namedattribute("level", level)) + + return IR.create_operation( + "sparse_tensor.storage_specifier.get", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`has_runtime_library` + +Returns a boolean value that indicates whether the sparsifier runs in +runtime library mode or not. For testing only! This operation is useful +for writing test cases that require different code depending on +runtime/codegen mode. + +# Example + +```mlir +%has_runtime = sparse_tensor.has_runtime_library +scf.if %has_runtime { + ... +} +``` +""" +function has_runtime_library(; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "sparse_tensor.has_runtime_library", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`load` + +Rematerializes a tensor from the underlying sparse storage format of the +given tensor. This is similar to the `bufferization.to_tensor` operation +in the sense that it provides a bridge between a bufferized world view +and a tensor world view. Unlike the `bufferization.to_tensor` operation, +however, this sparse operation is used only temporarily to maintain a +correctly typed intermediate representation during progressive +bufferization. + +The `hasInserts` attribute denote whether insertions to the underlying +sparse storage format may have occurred, in which case the underlying +sparse storage format needs to be finalized. Otherwise, the operation +simply folds away. + +Note that this operation is \"impure\" in the sense that even though +the result is modeled through an SSA value, the operation relies on +a proper context of materializing and inserting the tensor value. + +Examples: + +```mlir +%result = sparse_tensor.load %tensor : tensor<8xf64, #SV> + +%1 = sparse_tensor.load %0 hasInserts : tensor<16x32xf32, #CSR> +``` +""" +function load( + tensor::Value; + result=nothing::Union{Nothing,IR.Type}, + hasInserts=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(hasInserts) && push!(_attributes, namedattribute("hasInserts", hasInserts)) + + return IR.create_operation( + "sparse_tensor.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`lvl` + +The `sparse_tensor.lvl` behaves similar to `tensor.dim` operation. +It takes a sparse tensor and a level operand of type `index` and returns +the size of the requested level of the given sparse tensor. +If the sparse tensor has an identity dimension to level mapping, it returns +the same result as `tensor.dim`. +If the level index is out of bounds, the behavior is undefined. + +# Example + +```mlir +#BSR = #sparse_tensor.encoding<{ + map = ( i, j ) -> + ( i floordiv 2 : dense, + j floordiv 3 : compressed, + i mod 2 : dense, + j mod 3 : dense + ) +}> + +// Always returns 2 (4 floordiv 2), can be constant folded: +%c0 = arith.constant 0 : index +%x = sparse_tensor.lvl %A, %c0 : tensor<4x?xf32, #BSR> + +// Return the dynamic dimension of %A computed by %j mod 3. +%c1 = arith.constant 1 : index +%y = sparse_tensor.lvl %A, %c1 : tensor<4x?xf32, #BSR> + +// Always return 3 (since j mod 3 < 3), can be constant fold +%c3 = arith.constant 3 : index +%y = sparse_tensor.lvl %A, %c3 : tensor<4x?xf32, #BSR> +``` +""" +function lvl( + source::Value, index::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[source, index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "sparse_tensor.lvl", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`new` + +Materializes a sparse tensor with contents taken from an opaque pointer +provided by `source`. For targets that have access to a file system, +for example, this pointer may be a filename (or file) of a sparse +tensor in a particular external storage format. The form of the operation +is kept deliberately very general to allow for alternative implementations +in the future, such as pointers to buffers or runnable initialization +code. The operation is provided as an anchor that materializes a properly +typed sparse tensor with inital contents into a computation. + +Reading in a symmetric matrix will result in just the lower/upper triangular +part of the matrix (so that only relevant information is stored). Proper +symmetry support for operating on symmetric matrices is still TBD. + +# Example + +```mlir +sparse_tensor.new %source : !Source to tensor<1024x1024xf64, #CSR> +``` +""" +function new(source::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "sparse_tensor.new", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`number_of_entries` + +Returns the number of entries that are stored in the given sparse tensor. +Note that this is typically the number of nonzero elements in the tensor, +but since explicit zeros may appear in the storage formats, the more +accurate nomenclature is used. + +# Example + +```mlir +%noe = sparse_tensor.number_of_entries %tensor : tensor<64x64xf64, #CSR> +``` +""" +function number_of_entries( + tensor::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "sparse_tensor.number_of_entries", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`out` + +Outputs the contents of a sparse tensor to the destination defined by an +opaque pointer provided by `dest`. For targets that have access to a file +system, for example, this pointer may specify a filename (or file) for output. +The form of the operation is kept deliberately very general to allow for +alternative implementations in the future, such as sending the contents to +a buffer defined by a pointer. + +Note that this operation is \"impure\" in the sense that its behavior +is solely defined by side-effects and not SSA values. + +# Example + +```mlir +sparse_tensor.out %t, %dest : tensor<1024x1024xf64, #CSR>, !Dest +``` +""" +function out(tensor::Value, dest::Value; location=Location()) + _results = IR.Type[] + _operands = Value[tensor, dest] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "sparse_tensor.out", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`print` + +Prints the individual components of a sparse tensors (the positions, +coordinates, and values components) to stdout for testing and debugging +purposes. This operation lowers to just a few primitives in a light-weight +runtime support to simplify supporting this operation on new platforms. + +# Example + +```mlir +sparse_tensor.print %tensor : tensor<1024x1024xf64, #CSR> +``` +""" +function print(tensor::Value; location=Location()) + _results = IR.Type[] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "sparse_tensor.print", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`push_back` + +Pushes `value` to the end of the given sparse tensor storage buffer +`inBuffer` as indicated by the value of `curSize` and returns the +new size of the buffer in `newSize` (`newSize = curSize + n`). +The capacity of the buffer is recorded in the memref type of `inBuffer`. +If the current buffer is full, then `inBuffer.realloc` is called before +pushing the data to the buffer. This is similar to std::vector push_back. + +The optional input `n` specifies the number of times to repeately push +the value to the back of the tensor. When `n` is a compile-time constant, +its value can\'t be less than 1. If `n` is a runtime value that is less +than 1, the behavior is undefined. Although using input `n` is semantically +equivalent to calling push_back n times, it gives compiler more chances to +to optimize the memory reallocation and the filling of the memory with the +same value. + +The `inbounds` attribute tells the compiler that the insertion won\'t go +beyond the current storage buffer. This allows the compiler to not generate +the code for capacity check and reallocation. The typical usage will be for +\"dynamic\" sparse tensors for which a capacity can be set beforehand. + +Note that this operation is \"impure\" in the sense that even though +the result is modeled through an SSA value, referencing the memref +through the old SSA value after this operation is undefined behavior. + +# Example + +```mlir +%buf, %newSize = sparse_tensor.push_back %curSize, %buffer, %val + : index, memref, f64 +``` + +```mlir +%buf, %newSize = sparse_tensor.push_back inbounds %curSize, %buffer, %val + : xindex, memref, f64 +``` + +```mlir +%buf, %newSize = sparse_tensor.push_back inbounds %curSize, %buffer, %val, %n + : xindex, memref, f64 +``` +""" +function push_back( + curSize::Value, + inBuffer::Value, + value::Value, + n=nothing::Union{Nothing,Value}; + outBuffer=nothing::Union{Nothing,IR.Type}, + newSize=nothing::Union{Nothing,IR.Type}, + inbounds=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[curSize, inBuffer, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(n) && push!(_operands, n) + !isnothing(outBuffer) && push!(_results, outBuffer) + !isnothing(newSize) && push!(_results, newSize) + !isnothing(inbounds) && push!(_attributes, namedattribute("inbounds", inbounds)) + + return IR.create_operation( + "sparse_tensor.push_back", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`reduce` + +Defines a computation with a `linalg.generic` operation that takes two +operands and an identity value and reduces all stored values down to a +single result based on the computation in the region. + +The region must contain exactly one block taking two arguments. The block +must end with a sparse_tensor.yield and the output must match the input +argument types. + +Note that this operation is only required for custom reductions beyond +the standard reduction operations (add, sub, or, xor) that can be +sparsified by merely reducing the stored values. More elaborate reduction +operations (mul, and, min, max, etc.) would need to account for implicit +zeros as well. They can still be handled using this custom reduction +operation. The `linalg.generic` `iterator_types` defines which indices +are being reduced. When the associated operands are used in an operation, +a reduction will occur. The use of this explicit `reduce` operation +is not required in most cases. + +Example of Matrix->Vector reduction using max(product(x_i), 100): + +```mlir +%cf1 = arith.constant 1.0 : f64 +%cf100 = arith.constant 100.0 : f64 +%C = tensor.empty(...) +%0 = linalg.generic #trait + ins(%A: tensor) + outs(%C: tensor) { + ^bb0(%a: f64, %c: f64) : + %result = sparse_tensor.reduce %c, %a, %cf1 : f64 { + ^bb0(%arg0: f64, %arg1: f64): + %0 = arith.mulf %arg0, %arg1 : f64 + %cmp = arith.cmpf \"ogt\", %0, %cf100 : f64 + %ret = arith.select %cmp, %cf100, %0 : f64 + sparse_tensor.yield %ret : f64 + } + linalg.yield %result : f64 +} -> tensor +``` +""" +function reduce( + x::Value, + y::Value, + identity::Value; + output=nothing::Union{Nothing,IR.Type}, + region::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[x, y, identity] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "sparse_tensor.reduce", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`reinterpret_map` + +Reinterprets the dimension-to-level and level-to-dimension map specified in +`source` according to the type of `dest`. +`reinterpret_map` is a no-op and is introduced merely to resolve type conflicts. +It does not make any modification to the source tensor and source/dest tensors +are considered to be aliases. + +`source` and `dest` tensors are \"reinterpretable\" if and only if they have +the exactly same storage at a low level. +That is, both `source` and `dest` has the same number of levels and level types, +and their shape is consistent before and after `reinterpret_map`. + +# Example +```mlir +#CSC = #sparse_tensor.encoding<{ + map = (d0, d1) -> (d1: dense, d0: compressed) +}> +#CSR = #sparse_tensor.encoding<{ + map = (d0, d1) -> (d0: dense, d1: compressed) +}> +%t1 = sparse_tensor.reinterpret_map %t0 : tensor<3x4xi32, #CSC> to tensor<4x3xi32, #CSR> + +#BSR = #sparse_tensor.encoding<{ + map = ( i, j ) -> ( i floordiv 2 : dense, + j floordiv 3 : compressed, + i mod 2 : dense, + j mod 3 : dense + ) +}> +#DSDD = #sparse_tensor.encoding<{ + map = (i, j, k, l) -> (i: dense, j: compressed, k: dense, l: dense) +}> +%t1 = sparse_tensor.reinterpret_map %t0 : tensor<6x12xi32, #BSR> to tensor<3x4x2x3xi32, #DSDD> +``` +""" +function reinterpret_map(source::Value; dest::IR.Type, location=Location()) + _results = IR.Type[dest,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "sparse_tensor.reinterpret_map", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reorder_coo` + +Reorders the input COO to the same order as specified by the output format. +E.g., reorder an unordered COO into an ordered one. + +The input and result COO tensor must have the same element type, position type and +coordinate type. At the moment, the operation also only supports ordering +input and result COO with the same dim2lvl map. + +# Example + +```mlir +%res = sparse_tensor.reorder_coo quick_sort %coo : tensor to + tensor + +``` +""" +function reorder_coo(input_coo::Value; result_coo::IR.Type, algorithm, location=Location()) + _results = IR.Type[result_coo,] + _operands = Value[input_coo,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("algorithm", algorithm),] + + return IR.create_operation( + "sparse_tensor.reorder_coo", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`select` + +Defines an evaluation within a `linalg.generic` operation that takes a single +operand and decides whether or not to keep that operand in the output. + +A single region must contain exactly one block taking one argument. The block +must end with a sparse_tensor.yield and the output type must be boolean. + +Value threshold is an obvious usage of the select operation. However, by using +`linalg.index`, other useful selection can be achieved, such as selecting the +upper triangle of a matrix. + +Example of selecting A >= 4.0: + +```mlir +%C = tensor.empty(...) +%0 = linalg.generic #trait + ins(%A: tensor) + outs(%C: tensor) { + ^bb0(%a: f64, %c: f64) : + %result = sparse_tensor.select %a : f64 { + ^bb0(%arg0: f64): + %cf4 = arith.constant 4.0 : f64 + %keep = arith.cmpf \"uge\", %arg0, %cf4 : f64 + sparse_tensor.yield %keep : i1 + } + linalg.yield %result : f64 +} -> tensor +``` + +Example of selecting lower triangle of a matrix: + +```mlir +%C = tensor.empty(...) +%1 = linalg.generic #trait + ins(%A: tensor) + outs(%C: tensor) { + ^bb0(%a: f64, %c: f64) : + %row = linalg.index 0 : index + %col = linalg.index 1 : index + %result = sparse_tensor.select %a : f64 { + ^bb0(%arg0: f64): + %keep = arith.cmpf \"olt\", %col, %row : f64 + sparse_tensor.yield %keep : i1 + } + linalg.yield %result : f64 +} -> tensor +``` +""" +function select( + x::Value; output=nothing::Union{Nothing,IR.Type}, region::Region, location=Location() +) + _results = IR.Type[] + _operands = Value[x,] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "sparse_tensor.select", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`storage_specifier_set` + +Set the field of the storage specifier to the given input value. Returns +the updated storage_specifier as a new SSA value. + +Example of updating the sizes of the coordinates array for level 0: + +```mlir +%0 = sparse_tensor.storage_specifier.set %arg0 crd_mem_sz at 0 with %new_sz + : !sparse_tensor.storage_specifier<#COO> +``` +""" +function storage_specifier_set( + specifier::Value, + value::Value; + result=nothing::Union{Nothing,IR.Type}, + specifierKind, + level=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[specifier, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("specifierKind", specifierKind),] + !isnothing(result) && push!(_results, result) + !isnothing(level) && push!(_attributes, namedattribute("level", level)) + + return IR.create_operation( + "sparse_tensor.storage_specifier.set", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`sort` + +Sorts the `xs` values along with some `ys` values that are put in a single linear +buffer `xy`. The affine map attribute `perm_map` specifies the permutation to be +applied on the `xs` before comparison, the rank of the permutation map +also specifies the number of `xs` values in `xy`. +The optional index attribute `ny` provides the number of `ys` values in `xy`. +When `ny` is not explicitly specified, its value is 0. +This instruction supports a more efficient way to store the COO definition +in sparse tensor type. + +The buffer xy should have a dimension not less than n * (rank(perm_map) + ny) while the +buffers in `ys` should have a dimension not less than `n`. The behavior of +the operator is undefined if this condition is not met. + +# Example + +```mlir +sparse_tensor.sort insertion_sort_stable %n, %x { perm_map = affine_map<(i,j) -> (j,i)> } + : memref +``` +""" +function sort( + n::Value, + xy::Value, + ys::Vector{Value}; + perm_map, + ny=nothing, + algorithm, + location=Location(), +) + _results = IR.Type[] + _operands = Value[n, xy, ys...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("perm_map", perm_map), namedattribute("algorithm", algorithm) + ] + !isnothing(ny) && push!(_attributes, namedattribute("ny", ny)) + + return IR.create_operation( + "sparse_tensor.sort", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`storage_specifier_init` + +Returns an initial storage specifier value. A storage specifier +value holds the level-sizes, position arrays, coordinate arrays, +and the value array. +If this is a specifier for slices, it also holds the extra strides/offsets +for each tensor dimension. + +TODO: The sparse tensor slice support is currently in a unstable state, and +is subject to change in the future. + +# Example + +```mlir +#CSR = #sparse_tensor.encoding<{ + map = (i, j) -> (i : dense, j : compressed) +}> +#CSR_SLICE = #sparse_tensor.encoding<{ + map = (d0 : #sparse_tensor, + d1 : #sparse_tensor) -> + (d0 : dense, d1 : compressed) +}> + +%0 = sparse_tensor.storage_specifier.init : !sparse_tensor.storage_specifier<#CSR> +%1 = sparse_tensor.storage_specifier.init with %src + : !sparse_tensor.storage_specifier<#CSR> to + !sparse_tensor.storage_specifier<#CSR_SLICE> +``` +""" +function storage_specifier_init( + source=nothing::Union{Nothing,Value}; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(source) && push!(_operands, source) + + return IR.create_operation( + "sparse_tensor.storage_specifier.init", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`coordinates_buffer` + +Returns the linear coordinates array for a sparse tensor with +a trailing COO region with at least two levels. It is an error +if the tensor doesn\'t contain such a COO region. This is similar +to the `bufferization.to_memref` operation in the sense that it +provides a bridge between a tensor world view and a bufferized +world view. Unlike the `bufferization.to_memref` operation, +however, this operation actually lowers into code that extracts +the linear coordinates array from the sparse storage scheme that +stores the coordinates for the COO region as an array of structures. +For example, a 2D COO sparse tensor with two non-zero elements at +coordinates (1, 3) and (4, 6) are stored in a linear buffer as +(1, 4, 3, 6) instead of two buffer as (1, 4) and (3, 6). + +Writing into the result of this operation is undefined behavior. + +# Example + +```mlir +%1 = sparse_tensor.coordinates_buffer %0 + : tensor<64x64xf64, #COO> to memref +``` +""" +function coordinates_buffer( + tensor::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "sparse_tensor.coordinates_buffer", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`coordinates` + +Returns the coordinates array of the tensor\'s storage at the given +level. This is similar to the `bufferization.to_memref` operation +in the sense that it provides a bridge between a tensor world view +and a bufferized world view. Unlike the `bufferization.to_memref` +operation, however, this sparse operation actually lowers into code +that extracts the coordinates array from the sparse storage itself +(either by calling a support library or through direct code). + +Writing into the result of this operation is undefined behavior. + +# Example + +```mlir +%1 = sparse_tensor.coordinates %0 { level = 1 : index } + : tensor<64x64xf64, #CSR> to memref +``` +""" +function coordinates( + tensor::Value; result=nothing::Union{Nothing,IR.Type}, level, location=Location() +) + _results = IR.Type[] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("level", level),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "sparse_tensor.coordinates", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`positions` + +Returns the positions array of the tensor\'s storage at the given +level. This is similar to the `bufferization.to_memref` operation +in the sense that it provides a bridge between a tensor world view +and a bufferized world view. Unlike the `bufferization.to_memref` +operation, however, this sparse operation actually lowers into code +that extracts the positions array from the sparse storage itself +(either by calling a support library or through direct code). + +Writing into the result of this operation is undefined behavior. + +# Example + +```mlir +%1 = sparse_tensor.positions %0 { level = 1 : index } + : tensor<64x64xf64, #CSR> to memref +``` +""" +function positions( + tensor::Value; result=nothing::Union{Nothing,IR.Type}, level, location=Location() +) + _results = IR.Type[] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("level", level),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "sparse_tensor.positions", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`slice_offset` + +Extracts the offset of the sparse tensor slice at the given dimension. + +Currently, sparse tensor slices are still a work in progress, and only +works when runtime library is disabled (i.e., running the sparsifier +with `enable-runtime-library=false`). + +# Example + +```mlir +%0 = tensor.extract_slice %s[%v1, %v2][64, 64][1, 1] : tensor<128x128xf64, #DCSR> + to tensor<64x64xf64, #Slice> + +%1 = sparse_tensor.slice.offset %0 at 0 : tensor<64x64xf64, #Slice> +%2 = sparse_tensor.slice.offset %0 at 1 : tensor<64x64xf64, #Slice> +// %1 = %v1 +// %2 = %v2 +``` +""" +function slice_offset( + slice::Value; offset=nothing::Union{Nothing,IR.Type}, dim, location=Location() +) + _results = IR.Type[] + _operands = Value[slice,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dim", dim),] + !isnothing(offset) && push!(_results, offset) + + return IR.create_operation( + "sparse_tensor.slice.offset", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`slice_stride` + +Extracts the stride of the sparse tensor slice at the given dimension. + +Currently, sparse tensor slices are still a work in progress, and only +works when runtime library is disabled (i.e., running the sparsifier +with `enable-runtime-library=false`). + +# Example + +```mlir +%0 = tensor.extract_slice %s[%v1, %v2][64, 64][%s1, %s2] : tensor<128x128xf64, #DCSR> + to tensor<64x64xf64, #Slice> + +%1 = sparse_tensor.slice.stride %0 at 0 : tensor<64x64xf64, #Slice> +%2 = sparse_tensor.slice.stride %0 at 1 : tensor<64x64xf64, #Slice> +// %1 = %s1 +// %2 = %s2 + +``` +""" +function slice_stride( + slice::Value; stride=nothing::Union{Nothing,IR.Type}, dim, location=Location() +) + _results = IR.Type[] + _operands = Value[slice,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dim", dim),] + !isnothing(stride) && push!(_results, stride) + + return IR.create_operation( + "sparse_tensor.slice.stride", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`values` + +Returns the values array of the sparse storage format for the given +sparse tensor, independent of the actual dimension. This is similar to +the `bufferization.to_memref` operation in the sense that it provides a bridge +between a tensor world view and a bufferized world view. Unlike the +`bufferization.to_memref` operation, however, this sparse operation actually +lowers into code that extracts the values array from the sparse storage +scheme (either by calling a support library or through direct code). + +Writing into the result of this operation is undefined behavior. + +# Example + +```mlir +%1 = sparse_tensor.values %0 : tensor<64x64xf64, #CSR> to memref +``` +""" +function values(tensor::Value; result=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "sparse_tensor.values", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`unary` + +Defines a computation with a `linalg.generic` operation that takes a single +operand and executes one of two regions depending on whether the operand is +nonzero (i.e. stored explicitly in the sparse storage format). + +Two regions are defined for the operation must appear in this order: +- present (elements present in the sparse tensor) +- absent (elements not present in the sparse tensor) + +Each region contains a single block describing the computation and result. +A non-empty block must end with a sparse_tensor.yield and the return type +must match the type of `output`. The primary region\'s block has one +argument, while the missing region\'s block has zero arguments. The +absent region may only generate constants or values already computed +on entry of the `linalg.generic` operation. + +A region may also be declared empty (i.e. `absent={}`), indicating that the +region does not contribute to the output. + +Due to the possibility of empty regions, i.e. lack of a value for certain +cases, the result of this operation may only feed directly into the output +of the `linalg.generic` operation or into into a custom reduction +`sparse_tensor.reduce` operation that follows in the same region. + +Example of A+1, restricted to existing elements: + +```mlir +%C = tensor.empty(...) : tensor +%0 = linalg.generic #trait + ins(%A: tensor) + outs(%C: tensor) { + ^bb0(%a: f64, %c: f64) : + %result = sparse_tensor.unary %a : f64 to f64 + present={ + ^bb0(%arg0: f64): + %cf1 = arith.constant 1.0 : f64 + %ret = arith.addf %arg0, %cf1 : f64 + sparse_tensor.yield %ret : f64 + } + absent={} + linalg.yield %result : f64 +} -> tensor +``` + +Example returning +1 for existing values and -1 for missing values: + +```mlir +%p1 = arith.constant 1 : i32 +%m1 = arith.constant -1 : i32 +%C = tensor.empty(...) : tensor +%1 = linalg.generic #trait + ins(%A: tensor) + outs(%C: tensor) { + ^bb0(%a: f64, %c: i32) : + %result = sparse_tensor.unary %a : f64 to i32 + present={ + ^bb0(%x: f64): + sparse_tensor.yield %p1 : i32 + } + absent={ + sparse_tensor.yield %m1 : i32 + } + linalg.yield %result : i32 +} -> tensor +``` + +Example showing a structural inversion (existing values become missing in +the output, while missing values are filled with 1): + +```mlir +%c1 = arith.constant 1 : i64 +%C = tensor.empty(...) : tensor +%2 = linalg.generic #trait + ins(%A: tensor) + outs(%C: tensor) { + ^bb0(%a: f64, %c: i64) : + %result = sparse_tensor.unary %a : f64 to i64 + present={} + absent={ + sparse_tensor.yield %c1 : i64 + } + linalg.yield %result : i64 +} -> tensor +``` +""" +function unary( + x::Value; + output::IR.Type, + presentRegion::Region, + absentRegion::Region, + location=Location(), +) + _results = IR.Type[output,] + _operands = Value[x,] + _owned_regions = Region[presentRegion, absentRegion] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "sparse_tensor.unary", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +Yields a value from within a `binary`, `unary`, `reduce`, +`select` or `foreach` block. + +# Example + +```mlir +%0 = sparse_tensor.unary %a : i64 to i64 { + present={ + ^bb0(%arg0: i64): + %cst = arith.constant 1 : i64 + %ret = arith.addi %arg0, %cst : i64 + sparse_tensor.yield %ret : i64 + } +} +``` +""" +function yield(results::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[results...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "sparse_tensor.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # sparse_tensor diff --git a/src/Dialects/19/Tensor.jl b/src/Dialects/19/Tensor.jl new file mode 100644 index 00000000..ae19ba7e --- /dev/null +++ b/src/Dialects/19/Tensor.jl @@ -0,0 +1,1462 @@ +module tensor + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`bitcast` + +Bitcast a tensor from one type to another type of equivalent element width. +If both are ranked, then the rank should be the same and static dimensions +should match. + +# Example + +```mlir +// Bitcast from unsigned to signed or signless integer. +%2 = tensor.bitcast %1 : tensor<4xui32> to tensor<4xi32> +``` +""" +function bitcast(source::Value; dest::IR.Type, location=Location()) + _results = IR.Type[dest,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tensor.bitcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cast` + +Convert a tensor from one type to an equivalent type without changing any +data elements. The source and destination types must both be tensor types +with the same element type. If both are ranked, then the rank should be the +same and static dimensions should match. The operation is invalid if +converting to a mismatching constant dimension. + +# Example + +```mlir +// Convert from unknown rank to rank 2 with unknown dimension sizes. +%2 = tensor.cast %1 : tensor<*xf32> to tensor + +// Convert to a type with more known dimensions. +%3 = tensor.cast %2 : tensor to tensor<4x?xf32> + +// Discard static dimension and rank information. +%4 = tensor.cast %3 : tensor<4x?xf32> to tensor +%5 = tensor.cast %4 : tensor to tensor<*xf32> +``` +""" +function cast(source::Value; dest::IR.Type, location=Location()) + _results = IR.Type[dest,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tensor.cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`collapse_shape` + +The `tensor.collapse_shape` op produces a new tensor of lower (or equal) +rank whose dimension sizes are a reassociation of the original `src` dimensions. + +A reassociation is defined as a continuous grouping of dimensions and is +represented by an array of DenseI64ArrayAttr attribute. The reassociation +maps are applied to the operand shape to obtain the result shape. + + +# Example + +```mlir +// Dimension collapse (i, j) -> i\' and k -> k\' +%b = tensor.collapse_shape %a [[0, 1], [2]] + : tensor into tensor +``` +""" +function collapse_shape(src::Value; result::IR.Type, reassociation, location=Location()) + _results = IR.Type[result,] + _operands = Value[src,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("reassociation", reassociation),] + + return IR.create_operation( + "tensor.collapse_shape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`concat` + +The \"concat\" operation constructs a tensor out of a variadic list of input +tensors, concatenated along a static dimension number. All inputs and the +result type must share the same rank. + +`dim` specifies the dimension along which to concatenate. The size of the +concatenated dimension in the result must be equal to the sum of the sizes +of the inputs along that dimension. All other dimensions in both the inputs +and result must be the same size. + +# Example + +```mlir +%0 = tensor.concat dim(0) %0, %1, %2 : + (tensor<3x6xf32>, tensor<3x6xf32>, tensor<1x6xf32) -> tensor<7x6xf32> + +// Dynamic + dynamic -> static +%0 = tensor.concat dim(1) %0, %1, %2 : + (tensor<3x?xf32>, tensor<3x2xf32>, tensor<3x?xf32) -> tensor<3x10xf32> +``` +""" +function concat(inputs::Vector{Value}; result::IR.Type, dim, location=Location()) + _results = IR.Type[result,] + _operands = Value[inputs...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dim", dim),] + + return IR.create_operation( + "tensor.concat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`dim` + +The `tensor.dim` operation takes a tensor and a dimension operand of type +`index`. It returns the size of the requested dimension of the given +tensor. If the dimension index is out of bounds, the behavior is undefined. + +The specified tensor type is that of the first operand. + +# Example + +```mlir +// Always returns 4, can be constant folded: +%c0 = arith.constant 0 : index +%x = tensor.dim %A, %c0 : tensor<4x?xf32> + +// Return the dynamic dimension of %A. +%c1 = arith.constant 1 : index +%y = tensor.dim %A, %c1 : memref<4x?xf32> + +// Equivalent generic form: +%x = \"tensor.dim\"(%A, %c0) : (memref<4x?xf32>, index) -> index +%y = \"tensor.dim\"(%A, %c1) : (memref<4x?xf32>, index) -> index +``` +""" +function dim( + source::Value, index::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[source, index] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "tensor.dim", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`empty` + +`tensor.empty` is an operation that defines a tensor of a particular shape. +The shape could be dynamic or static. The contents of the tensor are +unspecified and the only purpose of the op result is to materialize the +specified shape in IR and make it available to other transformations. + +`tensor.empty` is useful in transformations that expect destination style +ops. I.e., ops that implement `DestinationStyleOpInterface`. Ops that are +not in destination style can be made compatible with such transformations +with a `tensor.empty` destination. + +Note: This op can be lowered to a `bufferization.alloc_tensor`, at which +point it turns into an explicit buffer allocation. +""" +function empty(dynamicSizes::Vector{Value}; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[dynamicSizes...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tensor.empty", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`expand_shape` + +The `tensor.expand_shape` op produces a tensor of higher (or equal) +rank than the operand `src` whose dimension sizes are a reassociation of +`src`. + +A reassociation is defined as a continuous grouping of dimensions and is +represented with an array of DenseI64ArrayAttr attribute. The reassociation +maps applied to the result tensor with the higher rank must result in the +operand tensor with the smaller rank. + +The representation for the output shape supports a partially-static +specification via attributes specified through the `static_output_shape` +argument. A special sentinel value `ShapedType::kDynamic` encodes that the +corresponding entry has a dynamic value. There must be exactly as many SSA +inputs in `output_shape` as there are `ShapedType::kDynamic` entries in +`static_output_shape`. + +# Example + +```mlir +// Dimension expansion i -> (i\', j\') and (k) -> (k\') +%b = tensor.expand_shape %a [[0, 1], [2]] output_shape [%sz0, %sz1, 32] + : tensor into tensor +``` +""" +function expand_shape( + src::Value, + output_shape::Vector{Value}; + result::IR.Type, + reassociation, + static_output_shape, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[src, output_shape...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("reassociation", reassociation), + namedattribute("static_output_shape", static_output_shape), + ] + + return IR.create_operation( + "tensor.expand_shape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`extract` + +The `tensor.extract` op reads a ranked tensor and returns one element as +specified by the given indices. The result of the op is a value with the +same type as the elements of the tensor. The arity of indices must match +the rank of the accessed value. All indices should all be of `index` type. + +# Example + +```mlir +%4 = tensor.extract %t[%1, %2] : tensor<4x4xi32> +%5 = tensor.extract %rt[%1, %2] : tensor +``` +""" +function extract( + tensor::Value, + indices::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[tensor, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "tensor.extract", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`extract_slice` + +The \"extract_slice\" operation extract a tensor from another tensor as +specified by the operation\'s offsets, sizes and strides arguments. + +The extract_slice operation supports the following arguments: + +* source: the \"base\" tensor from which to extract a slice. +* offsets: tensor-rank number of offsets into the \"base\" tensor from which + to extract the slice. +* sizes: tensor-rank number of sizes which specify the sizes of the result + tensor type. +* strides: tensor-rank number of strides specifying subsampling in each + dimension. + +The representation based on offsets, sizes and strides support a +partially-static specification via attributes specified through the +`static_offsets`, `static_sizes` and `static_strides` arguments. A special +sentinel value ShapedType::kDynamic encodes that the corresponding entry has +a dynamic value. + +After buffer allocation, the \"extract_slice\" op is expected to lower into a +memref.subview op. + +An extract_slice operation may additionally reduce the rank of the resulting +tensor by removing dimensions that are statically known to be of size 1. +This rank-reduction behavior is not required by the op semantics: this +flexibility allows to progressively drop unit dimensions while lowering +between different flavors of ops on that operate on tensors. + +#### Verification vs Inference in the rank-reduced case + +Note that there may be multiple ways to infer a resulting rank-reduced type. + e.g. 1x6x1 could potentially rank-reduce to either 1x6 or 6x1 2-D shapes. + +To disambiguate, the inference helpers `inferCanonicalRankReducedResultType` +only drop the first unit dimensions, in order: + e.g. 1x6x1 rank-reduced to 2-D will infer the 6x1 2-D shape, but not 1x6. + +Verification however has access to result type and does not need to infer. +The verifier calls `isRankReducedType(getSource(), getResult())` to +determine whether the result type is rank-reduced from the source type. +This computes a so-called rank-reduction mask, consisting of dropped unit +dims, to map the rank-reduced type to the source type by dropping ones: + e.g. 1x6 is a rank-reduced version of 1x6x1 by mask {2} + 6x1 is a rank-reduced version of 1x6x1 by mask {0} + 1x2x1x4 is a rank-reduced version of 1x1x2x1x1x4x1 by mask {1, 4, 6} + (remaining common 1 dimensions are matched eagerly) + +# Example + +```mlir +// Rank-reducing extract_slice. +%1 = tensor.extract_slice %0[0, 0, 0][1, 16, 4][1, 1, 1] : + tensor<8x16x4xf32> to tensor<16x4xf32> +%3 = tensor.extract_slice %2[%o0, 4, %o2][1, %sz1, 1][1, %st1, 1] : + tensor<8x16x4xf32> to tensor<1x?xf32> +``` +""" +function extract_slice( + source::Value, + offsets::Vector{Value}, + sizes::Vector{Value}, + strides::Vector{Value}; + result::IR.Type, + static_offsets, + static_sizes, + static_strides, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[source, offsets..., sizes..., strides...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("static_offsets", static_offsets), + namedattribute("static_sizes", static_sizes), + namedattribute("static_strides", static_strides), + ] + push!( + _attributes, + operandsegmentsizes([1, length(offsets), length(sizes), length(strides)]), + ) + + return IR.create_operation( + "tensor.extract_slice", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`from_elements` + +Create a N-D tensor from a range of same-type arguments. The number of +provided `elements` should equal to the number of the elements in the +result type. The `elements` correspond to a flattened tensor. + +# Example + +```mlir +tensor.from_elements %a, %b, %c, %d, %e, %f : tensor<2x3xindex> +``` + +will result in a tensor + +[[%a, %b, %c] + [%d, %e, %f]] +""" +function from_elements(elements::Vector{Value}; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[elements...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tensor.from_elements", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`gather` + +The `gather` operation extracts a subset of the elements from a `source` +tensor at the given indices. + +In its most general form, the tensor of indices specifies all the coordinates +of every element to extract (i.e. COO format, without the payload). +The indices are expected to be confined to coordinate values that fit the +range of the `source` tensor, otherwise the behavior is undefined. + +The leading dimensions of the index tensor give the result tensor its leading +dimensions. The trailing dimensions of the result tensor are obtained from +the source tensor by omitting the dimensions specified in `gather_dims` +(rank-reducing semantics) or setting them to `1` (rank-preserving semantics) +(see examples). +The trailing dimension of the index tensor contains the coordinates and is +expected to have its size equal to the number of dimensions being gathered. +This convention allows an idiomatic specification and lowering of \"gathering +multiple N-D slices from the source tensor\". + +Note: in the examples below, we separate out the indexing part of the tensor +type by a whitespace for readability purposes. + +# Example + +```mlir + // For each 1x2 triple of coordinates in %indices, extract the + // element (i.e. 0-D subset) at the coordinates triple in %source. + // + %out = tensor.gather %source[%indices] gather_dims([0, 1, 2]) : + (tensor<4x4x4xf32>, tensor<1x2x 3xindex>) -> tensor<1x2x 1x1x1xf32> + + // Note: result type may be further rank-reduced to tensor<1x2x f32>. +``` + +A slice variant is provided to allow specifying whole slices of the source +tensor. + +# Example + +```mlir + // For each 5x6 singleton of coordinates in %indices, extract the 2-D + // slice %source[*, %indices[...]:%indices[...] + 1, *] with the indices + // corresponding to the `gather_dims` attribute specified by %indices. + // + %out = tensor.gather %source[%indices] gather_dims([1]) : + (tensor<3x4x5xf32>, tensor<6x7x 1xindex>) -> tensor<6x7x 3x1x5xf32> + + // Note: result type may be further rank-reduced to tensor<6x7x 3x5xf32>. +``` + +The dimensions specified in the gather_dims attribute are ones for which the +result tensor has size `1`. +I.e. if the source type is `axbxcxd` and the coordinates are [1, 3], then +the shape suffix is `ax1xcx1`. +Gather also allows rank-reducing semantics where the shape `ax1xcx1` can be +further simplified to `axc`. + +The elemental type of the indices tensor can be any integer type. +In the absence of target-specific or problem specific information the default +type one should use is `index`. + +This operation does not support unranked tensors. + +An optional `unique` unit attribute may be specified to indicate that the +coordinates in `indices` are statically guaranteed to be unique at runtime. +Incorrectly setting the `unique` attribute when the coordinates are not truly +unique is undefined behavior. + +Only full slices are meant to be supported by this op, if one desires +partial slices (e.g. strided windows) one should compose this op with other +tensor ops (e.g. tensor.extract_slice). This is to avoid a slippery slope of +complexity that would make the op unusable in practice. + +At the tensor-level, the index tensor is specified in an AoS form (i.e. +coordinate tuple is the most minor). It is the responsibility of further +lowerings and bufferiation to implement various concrete layouts. + +Note: As currently specified, the operation must lower to an abstraction that +performs copies to the output tensor. This is because the buffer type system +is currently not rich enough to allow multiple non-contiguous views in the +same type. This is visible more clearly in a notional buffer version of the +op: + +```mlir + // memref is a contiguous buffer of ?x4x1 elements. + // gather from random source slices must copy to the contiguous output. + %out = memref.gather %source[%indices] gather_dims([1]) : + (memref<4x4xf32>, memref) -> memref + + // Nested buffer support would allow gather to directly index into the + // source buffer (i.e. represent a jagged view into the source). + %out = memref.gather %source[%indices] gather_dims([1]) : + (memref<4x4xf32>, memref) -> memref> +``` +""" +function gather( + source::Value, + indices::Value; + result::IR.Type, + gather_dims, + unique=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[source, indices] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("gather_dims", gather_dims),] + !isnothing(unique) && push!(_attributes, namedattribute("unique", unique)) + + return IR.create_operation( + "tensor.gather", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`generate` + +This operation creates a dynamically sized tensor with elements of any type. +It expects one index operand per dynamic extent of the result tensor. + +The body region defines the tensor\'s elements. It takes index operands as +its region arguments that span the index space. The element at the given +position is yielded with the `yield` operation (see `YieldOp`). There is +no defined ordering to the invocations of the body. It is conceptually +a \"parallel map\" operation. + +# Example + +```mlir + %tnsr = tensor.generate %m, %n { + ^bb0(%i : index, %j : index, %k : index): + ... + yield %elem : f32 + } : tensor +``` +""" +function generate( + dynamicExtents::Vector{Value}; result::IR.Type, body::Region, location=Location() +) + _results = IR.Type[result,] + _operands = Value[dynamicExtents...,] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tensor.generate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`insert` + +The `tensor.insert` op inserts a scalar into a ranked tensor `dest` as +specified by the operation\'s indices. + +It returns a copy of `dest` with the indexed position updated to the value +of `scalar`. + +The arity of `indices `must match the rank of the tensor `dest`. All +indices should be of `index` type. + +# Example + +```mlir +%4 = tensor.insert %t into %dest[%1, %2] : tensor<4x4xi32> +%5 = tensor.insert %rt into %dest[%1, %2] : tensor +``` +""" +function insert( + scalar::Value, + dest::Value, + indices::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[scalar, dest, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "tensor.insert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`insert_slice` + +The \"insert_slice\" operation insert a tensor `source` into another +tensor `dest` as specified by the operation\'s offsets, sizes and strides +arguments. + +It returns a copy of `dest` with the proper slice updated with the value +of `source`. + +The insert_slice operation supports the following arguments: + +* source: the tensor that is inserted. +* dest: the tensor into which the source tensor is inserted. +* offsets: tensor-rank number of offsets into the `dest` tensor into which + the slice is inserted. +* sizes: tensor-rank number of sizes which specify the sizes of the source + tensor type. +* strides: tensor-rank number of strides that specify subsampling in each + dimension. + +The representation based on offsets, sizes and strides support a +partially-static specification via attributes specified through the +`static_offsets`, `static_sizes` and `static_strides` arguments. A special +sentinel value ShapedType::kDynamic encodes that the corresponding entry has +a dynamic value. + +After buffer allocation, the \"insert_slice\" op is expected to lower into a +memref.subview op. + +An insert_slice operation may additionally specify insertion into a tensor +of higher rank than the source tensor, along dimensions that are statically +known to be of size 1. +This rank-altering behavior is not required by the op semantics: this +flexibility allows to progressively drop unit dimensions while lowering +between different flavors of ops on that operate on tensors. +The rank-altering behavior of tensor.insert_slice matches the rank-reducing +behavior of tensor.extract_slice. + +#### Verification in the rank-reduced case + +The same verification discussion and mechanisms apply as for ExtractSliceOp. +Unlike ExtractSliceOp however, there is no need for a specific inference. + +# Example + +```mlir +// Rank-altering insert_slice. +%1 = tensor.insert_slice %t into %0[0, 0, 0][1, 16, 4][1, 1, 1] : + tensor<16x4xf32> into tensor<8x16x4xf32> +%3 = tensor.insert_slice %tt into %2[%o0, 4, %o2][1, %sz1, 1][1, %st1, 1] : + tensor<1x?xf32> into tensor<8x16x4xf32> +``` +""" +function insert_slice( + source::Value, + dest::Value, + offsets::Vector{Value}, + sizes::Vector{Value}, + strides::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + static_offsets, + static_sizes, + static_strides, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source, dest, offsets..., sizes..., strides...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("static_offsets", static_offsets), + namedattribute("static_sizes", static_sizes), + namedattribute("static_strides", static_strides), + ] + push!( + _attributes, + operandsegmentsizes([1, 1, length(offsets), length(sizes), length(strides)]), + ) + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "tensor.insert_slice", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`pack` + +The \"pack\" operation converts a source tensor of rank `n` into a result +tensor of rank `n + k` with a tiled and packed layout (maybe with padding) +and optionally transposes the tiled source tensor dimensions. + +`inner_dims_pos` (mandatory) specifies `k` source tensor dimensions that are +being tiled, where `0 < k <= n`. The order of the dimensions matters: + - The tiled dimensions (of size `inner_tiles`) are added to the end of the result +tensor in the order in which they appear in `inner_dims_pos`. + - `inner_dims_pos[i]` specifies the source tensor dimension tiled by +`inner_tiles[i]`. + +`inner_tiles` (mandatory) specifies `k` tile sizes. These tile sizes +correspond to the least significant (\"inner\") result tensor dimension sizes, +in the same order. Tile sizes can be static or dynamic. + +# Example If `inner_tiles = [16, 32]`, the result tensor has a shape of +`...x16x32`. If `inner_dims_pos = [0, 1]`, the 0th source dimension is tiled +by 16 and the 1st source dimension is tiled by 32. Other source dimensions +(if any) are not tiled. If `inner_dims_pos = [1, 0]`, the 1st dimension is +tiled by 16 and the 0th dimension is tiled by 32. + +# Example +```mlir +// NC to NCnc +%0 = tensor.pack %source inner_dims_pos = [0, 1] inner_tiles = [8, 32] + into %dest : tensor<128x256xf32> -> tensor<16x8 x 8x32 xf32> +// \\ / \\ / +// outer dims inner dims +``` + +`outer_dims_perm` (optional) specifies a permutation for the outer +dimensions. If specified, it must have `n` elements. + +# Example +```mlir +// CK to KCck +%0 = tensor.pack %source outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] + inner_tiles = [8, 32] into %dest + : tensor<128x256xf32> -> tensor<8x16 x 8x32 xf32> +// \\ / +// compare with \"NC to NCnc\": outer dims are transposed +``` + +`padding_value` specifies a padding value at the boundary on non-perfectly +divisible dimensions. Padding is optional: +- If absent, it is UB if the tile does not perfectly divide the dimension. +- If present, it will pad along high dimensions (high-padding) to make the + tile complete. + +# Example +```mlir +%0 = tensor.pack %arg0 padding_value(%pad : f32) outer_dims_perm = [2, 1, 0] + inner_dims_pos = [1] inner_tiles = [2] into %arg1 + : tensor<200x127x256xf32> -> tensor<256x64x200x2xf32> +// \\ +// padded and tiled dim +// +// Source dimension 1 is tiled. 64 does not divide 127 evenly, so 1 padded +// element is added at the end. +// +// Note: Only tiled dimensions can be padded. +``` +""" +function pack( + source::Value, + dest::Value, + padding_value=nothing::Union{Nothing,Value}; + inner_tiles::Vector{Value}, + result=nothing::Union{Nothing,IR.Type}, + outer_dims_perm=nothing, + inner_dims_pos, + static_inner_tiles, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source, dest, inner_tiles...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("inner_dims_pos", inner_dims_pos), + namedattribute("static_inner_tiles", static_inner_tiles), + ] + !isnothing(padding_value) && push!(_operands, padding_value) + push!( + _attributes, + operandsegmentsizes([1, 1, isnothing(padding_value) ? 0 : 1, length(inner_tiles)]), + ) + !isnothing(result) && push!(_results, result) + !isnothing(outer_dims_perm) && + push!(_attributes, namedattribute("outer_dims_perm", outer_dims_perm)) + + return IR.create_operation( + "tensor.pack", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`pad` + +`tensor.pad` is an operation that pads the `source` tensor +with given `low` and `high` padding config. + +The PadOp operation supports the following arguments: + +* source: the \"base\" tensor on which to pad. +* low: A list contains the padding along the start of each + dimension, i.e., how many padded values are prepended + to the beginning of the tensor in each dimension. +* high: A list contains the padding along the end of each + dimension, i.e., how many padded values are appended + to the end of the tensor in each dimension. +* nofold: indicates that the operation should not be folded when source and + result types are equal. + +The result tensor dimensions are `low[i]` + `dim[i]` + `high[i]` for each +dimension `i`. The number of elements of `low` and `high` must match the +rank of the input tensor. They can be either a constant or a dynamic value. + +The region of the `tensor.pad` operation returns the value to use +for the padding. The arguments of the region represent the index +of the source being accessed. There should be as many arguments as +the rank of the `source` tensor. The value `yield`-ed by the +region is used as the value of the view at the given position. + +If `nofold` is set, the padding operation will not be folded away even +if the source type and the padded type have the same static shape. This can +be used, e.g., for packing or promotion to faster memory. + +Example 1: add 3 zeros to the beginning and 5 zeros to the end of a 1D +tensor. + +```mlir + %arg0 = ... : tensor<10xi32> + %c0_i32 = arith.constant 0 : i32 + %padded = tensor.pad %arg0 low[3] high[5] { + ^bb0(%arg1: index): + tensor.yield %c0_i32 : i32 + } : tensor<10xi32> to tensor<18xi32> +``` + +Example 2: add 1 value to the beginning of dimension 0, 2 values to the end +of dimension 0, 2 values to the start of dimension 1, and 3 values to the +end of dimension 1. + +```mlir + %pad_value = ... : f32 + %0 = tensor.pad %0 low[1, 2] high[2, 3] { + ^bb0(%arg0 : index, %arg1 : index): + tensor.yield %pad_value : f32 + } : tensor to tensor +``` + +Example 3: + +```mlir + %pad_value = ... : f32 + %0 = tensor.pad %arg0 low[2, %arg1, 3, 3] high[3, 3, %arg1, 2] { + ^bb0(%arg2: index, %arg3: index, %arg4: index, %arg5: index): + tensor.yield %pad_value : f32 + } : tensor<1x2x2x?xf32> to tensor<6x?x?x?xf32> +``` + +Example 4: + +```mlir + %pad_value = ... : f32 + %0 = tensor.pad %arg0 low[0, 0] high[%ub0, %ub1] { + ^bb0(%arg1: index, %arg2: index): + tensor.yield %pad_value : f32 + } : tensor<2x3xf32> to tensor +``` + +Example 5: Force a padded value to be always exist with `nofold`, even +though the padding config specifies that no new elements will be added to +the tensor. + +```mlir + %pad_value = ... : f32 + %0 = tensor.pad %arg0 nofold low[0, 0] high[0, 0] { + ^bb0(%arg1: index, %arg2: index): + tensor.yield %pad_value : f32 + } : tensor<2x3xf32> to tensor<2x3xf32> +``` +""" +function pad( + source::Value, + low::Vector{Value}, + high::Vector{Value}; + result::IR.Type, + static_low, + static_high, + nofold=nothing, + region::Region, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[source, low..., high...] + _owned_regions = Region[region,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("static_low", static_low), namedattribute("static_high", static_high) + ] + push!(_attributes, operandsegmentsizes([1, length(low), length(high)])) + !isnothing(nofold) && push!(_attributes, namedattribute("nofold", nofold)) + + return IR.create_operation( + "tensor.pad", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`parallel_insert_slice` + +The `parallel_insert_slice` yields a subset tensor value to its parent +ParallelCombiningOpInterface. These subset tensor values are aggregated to +in some unspecified order into a full tensor value returned by the parent +parallel iterating op. +The `parallel_insert_slice` is one such op allowed in the +ParallelCombiningOpInterface op. + +Conflicting writes result in undefined semantics, in that the indices written +to by multiple parallel updates might contain data from any of the updates, +or even a malformed bit pattern. + +If an index is updated exactly once, the value contained at that index +in the resulting tensor will be equal to the value at a corresponding index +of a slice that was used for the updated. If an index is not updated at all, +its value will be equal to the one in the original tensor. + +This op does not create a new value, which allows maintaining a clean +separation between the subset and full tensor. + +Note that we cannot mark this operation as pure (Pures), even +though it has no side effects, because it will get DCEd during +canonicalization. + +The parallel_insert_slice operation supports the following arguments: + +* source: the tensor that is inserted. +* dest: the tensor into which the source tensor is inserted. +* offsets: tensor-rank number of offsets into the `dest` tensor into which + the slice is inserted. +* sizes: tensor-rank number of sizes which specify the sizes of the source + tensor type. +* strides: tensor-rank number of strides that specify subsampling in each + dimension. + +The representation based on offsets, sizes and strides support a +partially-static specification via attributes specified through the +`static_offsets`, `static_sizes` and `static_strides` arguments. A special +sentinel value ShapedType::kDynamic encodes that the corresponding entry has +a dynamic value. + +After buffer allocation, the \"parallel_insert_slice\" op is expected to lower +into a memref.subview op. + +A parallel_insert_slice operation may additionally specify insertion into a +tensor of higher rank than the source tensor, along dimensions that are +statically known to be of size 1. +This rank-altering behavior is not required by the op semantics: this +flexibility allows to progressively drop unit dimensions while lowering +between different flavors of ops on that operate on tensors. +The rank-altering behavior of tensor.parallel_insert_slice matches the +rank-reducing behavior of tensor.insert_slice and tensor.extract_slice. + +#### Verification in the rank-reduced case + +The same verification discussion and mechanisms apply as for ExtractSliceOp. +Unlike ExtractSliceOp however, there is no need for a specific inference. +""" +function parallel_insert_slice( + source::Value, + dest::Value, + offsets::Vector{Value}, + sizes::Vector{Value}, + strides::Vector{Value}; + static_offsets, + static_sizes, + static_strides, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source, dest, offsets..., sizes..., strides...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("static_offsets", static_offsets), + namedattribute("static_sizes", static_sizes), + namedattribute("static_strides", static_strides), + ] + push!( + _attributes, + operandsegmentsizes([1, 1, length(offsets), length(sizes), length(strides)]), + ) + + return IR.create_operation( + "tensor.parallel_insert_slice", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`rank` + +The `tensor.rank` operation takes a tensor operand and returns its rank. + +# Example + +```mlir +%0 = tensor.rank %arg0 : tensor<*xf32> +%1 = tensor.rank %arg1 : tensor +``` +""" +function rank(tensor::Value; result_0=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[tensor,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result_0) && push!(_results, result_0) + + return IR.create_operation( + "tensor.rank", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`reshape` + +The `reshape` operation converts a tensor from one type to an equivalent +type with a provided shape. The source and destination types are compatible +if both have the same element type, same number of elements. The following +combinations are possible: + +a. Source type is ranked or unranked. Shape argument has static size. +Result type is ranked. + +```mlir +// Reshape statically-shaped tensor. +%dst = tensor.reshape %src(%shape) + : (tensor<4x1xf32>, tensor<1xi32>) -> tensor<4xf32> +%dst0 = tensor.reshape %src(%shape0) + : (tensor<4x1xf32>, tensor<2xi32>) -> tensor<2x2xf32> +// Flatten unranked tensor. +%dst = tensor.reshape %src(%shape) + : (tensor<*xf32>, tensor<1xi32>) -> tensor +``` + +b. Source type is ranked or unranked. Shape argument has dynamic size. +Result type is unranked. + +```mlir +// Reshape dynamically-shaped 1D tensor. +%dst = tensor.reshape %src(%shape) + : (tensor, tensor) -> tensor<*xf32> +// Reshape unranked tensor. +%dst = tensor.reshape %src(%shape) + : (tensor<*xf32>, tensor) -> tensor<*xf32> +``` +""" +function reshape(source::Value, shape::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[source, shape] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tensor.reshape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`scatter` + +The `scatter` operation inserts a `source` tensor into a `dest` tensor at +the given indices. + +In its most general form, the tensor of indices specifies all the coordinates +of every element to insert (i.e. COO format, without the payload). +The indices are expected to be confined to coordinate values that fit the +range of the `dest` tensor, otherwise the behavior is undefined. + +The leading dimensions of the index tensor must match that of the dest +tensor. The trailing dimensions of the dest tensor must match those of the +source tensor by omitting the dimensions specified in scatter_dims +(rank-reducing semantics) or setting them to `1` (rank-preserving semantics) +(see examples). +This convention allows an idiomatic specification and lowering of +\"scattering multiple N-D slices into the dest tensor\". +The result type must match the type of the dest tensor. + +Note: in the examples below, we separate out the indexing part of the tensor +type by a whitespace for readability purposes. + +# Example + +```mlir + // For each 1x2 triple of coordinates in %indices, insert the + // element (i.e. 0-D subset) at the coordinates triple in %dest. + // + %out = tensor.scatter %source into %dest[%indices] + scatter_dims([0, 1, 2]) unique : + (tensor<1x2x 1x1x1xf32>, tensor<4x4x4xf32>, tensor<1x2x 3xindex>) + -> tensor<4x4x4xf32> + + // Note: source type may be further rank-reduced to tensor<1x2x f32>. +``` + +A slice variant is provided to allow specifying insertion of whole tensor +slices into the `dest` tensor. + +# Example + +```mlir + // For each 3 singleton of coordinates in %indices, insert the 2-D + // slice into %dest[*, %indices[...]:%indices[...] + 1, *] with the + // indices corresponding to the scatter_dims attribute specified by + // %indices. + // + %out = tensor.scatter %source into %dest[%indices] scatter_dims([1]) unique : + (tensor<3x 4x1x6xf32>, tensor<4x5x6xf32>, tensor<3x 1xindex>) + -> tensor<4x5x6xf32> +``` + +The dimensions specified in the scatter_dims attribute are ones for which the +source tensor has size `1`. +I.e. if the dest type is `axbxcxd` and the coordinates are [1, 3], then +the source type suffix is `ax1xcx1`. +Sactter also allows rank-reducing semantics where the shape `ax1xcx1` can be +further simplified to `axc`. + +The elemental type of the indices tensor can be any integer type. +In the absence of target-specific or problem specific information the default +type one should use is `index`. + +This operation does not support unranked tensors. + +A `unique` unit attribute must be be specified to indicate that the +coordinates are statically guaranteed to be unique at runtime. If coordinates +are not truly unique at runtime, the behavior is undefined. + +Only full slices are meant to be supported by this op, if one desires +partial slices (e.g. strided windows) one should compose this op with other +tensor ops (e.g. tensor.insert_slice). This is to avoid a slippery slope of +complexity that would make the op unusable in practice. + +At the tensor-level, the index tensor is specified in an AoS form (i.e. +coordinate tuple is the most minor). It is the responsibility of further +lowerings and bufferiation to implement various concrete layouts. + +Note: As currently specified, the operation must lower to an abstraction that +performs copies to the output tensor. This is because the buffer type system +is currently not rich enough to allow multiple non-contiguous views in the +same type. This is visible more clearly in a notional buffer version of the +op: + +```mlir + // memref is a contiguous buffer of ?x4 elements, scatter into + // random dest slices must copy to the contiguous dest. + // + some_side_effecting_op_writing_into %source, ...: memref<3x 4xf32> + memref.scatter %source into %dest[%indices] scatter_dims([1]) unique : + (memref<3x 4xf32>, memref, memref) + + // Nested buffer support in the producing op would allow writing directly + // into the dest buffer. + %v = some_nested_buffer_view_op %dest[%indices] scatter_dims([1]) unique : + memref> + some_side_effecting_op_writing_into %v, ...: memref> +``` +""" +function scatter( + source::Value, + dest::Value, + indices::Value; + result::IR.Type, + scatter_dims, + unique=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[source, dest, indices] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("scatter_dims", scatter_dims),] + !isnothing(unique) && push!(_attributes, namedattribute("unique", unique)) + + return IR.create_operation( + "tensor.scatter", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`splat` + +Broadcast the operand to all elements of the result tensor. The operand is +required to be of integer/index/float type. + +An additional argument of type `index` must be provided for each dynamic +dimension present in the result type. + +Example for a statically shaped tensor: + +```mlir +%s = arith.constant 1.0 : f32 +%t = tensor.splat %s : tensor<8x16xf32> +``` + +Example for a tensor containing dynamic dimensions: + +```mlir +// Broadcasts %s to a 3D dynamically shaped tensor, with %m and %n binding +// to dimensions 0 and 2 of the resulting tensor, respectively. +%m = arith.constant 10 : index +%n = arith.constant 30 : index +%t = tensor.splat %s[%m, %n] : tensor +``` +""" +function splat( + input::Value, dynamicSizes::Vector{Value}; aggregate::IR.Type, location=Location() +) + _results = IR.Type[aggregate,] + _operands = Value[input, dynamicSizes...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tensor.splat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`unpack` + +The \"unpack\" operation converts a source tensor of rank `n` with a tiled and +packed layout to a result tensor of rank `n - k`. + +`inner_dims_pos` (mandatory) specifies `k` source tensor dimensions with +which the last `k` source tensor dimensions are combined, where +`0 < k <= n/2`. Each `inner_dims_pos` element must be `>= 0` and `< n - k`. +The order of the dimensions in `inner_dims_pos` matters: dimension +`inner_dims_pos[i]` is combined with dimension `n - k + i` (assuming that +`outer_dims_perm` is not specified). + +`inner_tiles` (mandatory) specifies `k` tile sizes. These tile sizes +correspond to the least significant (\"inner\") source tensor dimension sizes. +The behavior of this op is undefined if: +- `inner_tiles` do not exactly match with the corresponding source tensor + dimension sizes. +- Or, `inner_tiles[i]` does not divide the size of dimension + `inner_dims_pos[i]` (assuming that `outer_dims_perm` is not specified) + evenly. + +`outer_dims_perm` (optional) specifies a permutation for the outer +dimensions. If specified, it must have `n - k` elements. If specified, this +permutation is applied before combining any dimensions. + +# Example + +```mlir +// NCnc to NC: +%0 = tensor.unpack %source inner_dims_pos = [0, 1] inner_tiles = [8, 32] + into %dest : tensor<16x8x8x32xf32> -> tensor<128x256xf32> + +// CK to KCck: +%0 = tensor.unpack %source outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] + inner_tiles = [8, 32] into %dest + : tensor<8x16x8x32xf32> -> tensor<128x256xf32> +``` +""" +function unpack( + source::Value, + dest::Value, + inner_tiles::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + outer_dims_perm=nothing, + inner_dims_pos, + static_inner_tiles, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source, dest, inner_tiles...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("inner_dims_pos", inner_dims_pos), + namedattribute("static_inner_tiles", static_inner_tiles), + ] + !isnothing(result) && push!(_results, result) + !isnothing(outer_dims_perm) && + push!(_attributes, namedattribute("outer_dims_perm", outer_dims_perm)) + + return IR.create_operation( + "tensor.unpack", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`yield` + +This operation is used to yield a single value from a within a region. It +is used to create dynamically sized tensors +(see `tensor.generate` and `tensor.pad` ops). +""" +function yield(value::Value; location=Location()) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tensor.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # tensor diff --git a/src/Dialects/19/Tosa.jl b/src/Dialects/19/Tosa.jl new file mode 100644 index 00000000..6c4b900c --- /dev/null +++ b/src/Dialects/19/Tosa.jl @@ -0,0 +1,2311 @@ +module tosa + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`abs` + +Elementwise absolute value operation + +# Example + +```mlir +%out = tosa.abs(%in) : (tensor<21x3xf32>) -> tensor<21x3xf32> +``` +""" +function abs(input1::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.abs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`add` + +Elementwise addition of input1 and input2. Axis of size 1 will be broadcast, +as necessary. + +# Example + +```mlir +// Elementwise addition. +%out = tosa.add %in1, %in2 : tensor<12x6xf32>, tensor<12x6xf32> -> tensor<12x6xf32> + +// Elementwise addition with broadcasting. +%out = tosa.add %in1, %in2 : tensor<12x6xsi32>, tensor<1x1xsi32> -> tensor<12x6xsi32> +``` +""" +function add(input1::Value, input2::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.add", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_scale` + +Applies rescaling for fixed point values. This behavior is replicated in +multiple quantized operations (mul, convolution, rescale, matmul, pooling). + +The commonplace implementation is to use i64 operations to avoid integer +overflow with target specific implementations can use native operations to +avoid wider than necessary types. +""" +function apply_scale( + value::Value, + multiplier::Value, + shift::Value; + output::IR.Type, + double_round, + location=Location(), +) + _results = IR.Type[output,] + _operands = Value[value, multiplier, shift] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("double_round", double_round),] + + return IR.create_operation( + "tosa.apply_scale", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`argmax` + +This returns the index with the largest value across the given axis of the +input tensor. +""" +function argmax(input::Value; output::IR.Type, axis, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("axis", axis),] + + return IR.create_operation( + "tosa.argmax", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`arithmetic_right_shift` + +Elementwise arithmetic right shift of input1 by the amount specified in +input2. Axis of size 1 will be broadcast, as necessary. +""" +function arithmetic_right_shift( + input1::Value, input2::Value; output::IR.Type, round, location=Location() +) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("round", round),] + + return IR.create_operation( + "tosa.arithmetic_right_shift", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`avg_pool2d` + +This performs an average pooling over the given input tensor. A sliding +window of size given by is passed over the input tensor, with +the mean value being placed in the output tensor. +""" +function avg_pool2d( + input::Value; + output::IR.Type, + kernel, + stride, + pad, + acc_type, + quantization_info=nothing, + location=Location(), +) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("kernel", kernel), + namedattribute("stride", stride), + namedattribute("pad", pad), + namedattribute("acc_type", acc_type), + ] + !isnothing(quantization_info) && + push!(_attributes, namedattribute("quantization_info", quantization_info)) + + return IR.create_operation( + "tosa.avg_pool2d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bitwise_and` + +Elementwise bitwise AND of input1 and input2. Axis of size 1 +will be broadcast as necessary. +""" +function bitwise_and(input1::Value, input2::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.bitwise_and", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bitwise_not` + +Elementwise bitwise NOT of input tensor. +""" +function bitwise_not(input1::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.bitwise_not", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bitwise_or` + +Elementwise bitwise OR of input1 and input2. Axis of size 1 will be +broadcast as necessary. +""" +function bitwise_or(input1::Value, input2::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.bitwise_or", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bitwise_xor` + +Elementwise bitwise XOR of input1 and input2. Axis of size 1 will be +broadcast as necessary. +""" +function bitwise_xor(input1::Value, input2::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.bitwise_xor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cast` + +Performs a set of permissible cast operations + +| Mode | Input | Output | +|--------------------------|---------|---------| +| signed 8 to bool | int8 | Boolean | +| signed 16 to bool | int16 | Boolean | +| signed 32 to bool | int32 | Boolean | +| bool to 8 | Boolean | int8 | +| bool to 16 | Boolean | int16 | +| bool to 32 | Boolean | int32 | +| signed 8 to signed 16 | int8 | int16 | +| signed 8 to signed 32 | int8 | int32 | +| signed 16 to signed 8 | int16 | int8 | +| signed 16 to signed 32 | int16 | int32 | +| signed 32 to signed 8 | int32 | int8 | +| signed 32 to signed 16 | int32 | int16 | +| float to signed 8 | float | int8 | +| float to signed 16 | float | int16 | +| signed 8 to float | int8 | float | +| signed 16 to float | int16 | float | +| float 32 to float 64 | float32 | float64 | +| float 64 to float 32 | float64 | float32 | +""" +function cast(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`ceil` + +Elementwise ceiling operation +""" +function ceil(input1::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.ceil", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`clamp` + +Clamp to an arbitrary minimum and maximum value. +Maximum and minimum values are specified as values in the range of the +input type. +No zero point subtraction is done to the values, thus to clamp to the zero +point value, the zero point itself should be supplied as the minimum value. +""" +function clamp( + input::Value; output::IR.Type, min_int, max_int, min_fp, max_fp, location=Location() +) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("min_int", min_int), + namedattribute("max_int", max_int), + namedattribute("min_fp", min_fp), + namedattribute("max_fp", max_fp), + ] + + return IR.create_operation( + "tosa.clamp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`clz` + +Elementwise count leading zeros operation +""" +function clz(input1::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.clz", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`concat` + +Concatenate a variadic amount of tensors along a given axis. No data +conversion happens during a concat operation. +""" +function concat( + input1::Vector{Value}; output=nothing::Union{Nothing,IR.Type}, axis, location=Location() +) + _results = IR.Type[] + _operands = Value[input1...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("axis", axis),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "tosa.concat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`const_` + +A node containing constant data for use as the input to an operation. May +hold data in any of the supported data formats. + +# Example + +```mlir +// Generic form +%out = \"tosa.const\"() {value = dense<0> : tensor<2x3xi32>} : () -> tensor<2x3xi32> +``` +""" +function const_(; output=nothing::Union{Nothing,IR.Type}, value, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "tosa.const", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`conv2d` + +Performs a 2D convolution over the given tensor input, using the weight +tensor. +""" +function conv2d( + input::Value, + weight::Value, + bias::Value; + output::IR.Type, + pad, + stride, + dilation, + quantization_info=nothing, + local_bound=nothing, + location=Location(), +) + _results = IR.Type[output,] + _operands = Value[input, weight, bias] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("pad", pad), + namedattribute("stride", stride), + namedattribute("dilation", dilation), + ] + !isnothing(quantization_info) && + push!(_attributes, namedattribute("quantization_info", quantization_info)) + !isnothing(local_bound) && + push!(_attributes, namedattribute("local_bound", local_bound)) + + return IR.create_operation( + "tosa.conv2d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`conv3d` + +Performs a 3D convolution over the given input tensor. +""" +function conv3d( + input::Value, + weight::Value, + bias::Value; + output::IR.Type, + pad, + stride, + dilation, + quantization_info=nothing, + local_bound=nothing, + location=Location(), +) + _results = IR.Type[output,] + _operands = Value[input, weight, bias] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("pad", pad), + namedattribute("stride", stride), + namedattribute("dilation", dilation), + ] + !isnothing(quantization_info) && + push!(_attributes, namedattribute("quantization_info", quantization_info)) + !isnothing(local_bound) && + push!(_attributes, namedattribute("local_bound", local_bound)) + + return IR.create_operation( + "tosa.conv3d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cos` + +Elementwise cosine operation for values given in radians. +""" +function cos(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.cos", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`custom` + +Hardware implementing TOSA may choose to add additional custom operators +that are not expressed in the existing TOSA operations. These operators are +not expected to be portable across TOSA implementations. The input and +output signatures must be expressed in the corresponding TOSA node. + +`operator_name` is a string that tells the backend which custom operator is +being called. + +`domain_name` is a string identifier which can help avoid name collisions on +the identifier field. + +`implementation_attrs` is a string which is a backend and identifier specific +set of attributes to the custom operator. + +`inputs` is the set of tensor inputs to the custom operator. + +`outputs is the list of tensors returned by the operator. The number of operators +is backend specific. + +# Example + +```mlir +%out = tosa.custom %in {domain_name = \"tosa_mlir_test\", operator_name = + \"custom_test\", implementation_attrs = \"\"}: (tensor<10xi32>) -> + (tensor<10xi32>) +``` +""" +function custom( + inputs::Vector{Value}; + outputs::Vector{IR.Type}, + operator_name, + domain_name, + implementation_attrs, + location=Location(), +) + _results = IR.Type[outputs...,] + _operands = Value[inputs...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("operator_name", operator_name), + namedattribute("domain_name", domain_name), + namedattribute("implementation_attrs", implementation_attrs), + ] + + return IR.create_operation( + "tosa.custom", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`depthwise_conv2d` + +Performs 2D convolutions separately over each channel of the given tensor +input, using the weight tensor. +""" +function depthwise_conv2d( + input::Value, + weight::Value, + bias::Value; + output::IR.Type, + pad, + stride, + dilation, + quantization_info=nothing, + local_bound=nothing, + location=Location(), +) + _results = IR.Type[output,] + _operands = Value[input, weight, bias] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("pad", pad), + namedattribute("stride", stride), + namedattribute("dilation", dilation), + ] + !isnothing(quantization_info) && + push!(_attributes, namedattribute("quantization_info", quantization_info)) + !isnothing(local_bound) && + push!(_attributes, namedattribute("local_bound", local_bound)) + + return IR.create_operation( + "tosa.depthwise_conv2d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`equal` + +Elementwise comparison operation +""" +function equal( + input1::Value, + input2::Value; + output=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "tosa.equal", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`erf` + +Gauss error function: \$ erf(x) = \\frac{2}{\\sqrt(\\pi)} \\int_{0}^{x} e^{-t^2} \\,dt \$ +For quantized integer data types, the TABLE operator should be used instead +with the following definition. The erf_table has 513 entries each of +16-bit/8-bit precision and covering the input range -4.0 to +4.0 in steps of 1/64. +""" +function erf(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.erf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`exp` + +Elementwise e to the x operation +""" +function exp(input1::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.exp", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fft2d` + +Performs a batched complex 2D Fast Fourier Transform over the input. The +complex input values are constructed from the corresponding values in the +input_real and input_imag tensors. The resulting values in the output are +split into the output_real and output_imag tensors. No normalization is +applied on either the forward or inverse versions of the operation. + +# Example + +```mlir + %out_real, %out_imag = tosa.fft2d %in_real, %in_imag : (tensor<8x9xf32>, tensor<8x9xf32>) -> (tensor<8x9xf32>, tensor<8x9xf32>) +``` +""" +function fft2d( + input_real::Value, + input_imag::Value; + output_real::IR.Type, + output_imag::IR.Type, + inverse, + local_bound=nothing, + location=Location(), +) + _results = IR.Type[output_real, output_imag] + _operands = Value[input_real, input_imag] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("inverse", inverse),] + !isnothing(local_bound) && + push!(_attributes, namedattribute("local_bound", local_bound)) + + return IR.create_operation( + "tosa.fft2d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`floor` + +Elementwise floor operation +""" +function floor(input1::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.floor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fully_connected` + +Performs a fully connected network. +""" +function fully_connected( + input::Value, + weight::Value, + bias::Value; + output::IR.Type, + quantization_info=nothing, + location=Location(), +) + _results = IR.Type[output,] + _operands = Value[input, weight, bias] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(quantization_info) && + push!(_attributes, namedattribute("quantization_info", quantization_info)) + + return IR.create_operation( + "tosa.fully_connected", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`gather` + +Generate a tensor for which each element in the output is a slice of the +values tensor based on the value of indices. +""" +function gather(values::Value, indices::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[values, indices] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.gather", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`greater_equal` + +Elementwise comparison operation +""" +function greater_equal(input1::Value, input2::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.greater_equal", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`greater` + +Elementwise greater than comparison operation +""" +function greater(input1::Value, input2::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.greater", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`identity` + +Returns a tensor with the same shape, size, type +and content as the input. +""" +function identity(input1::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.identity", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cond_if` + +Evaluates a Boolean condition and then takes one of two distinct execution +paths. This implements the semantic If-then-else structure. +""" +function cond_if( + cond::Value, + inputs::Vector{Value}; + output::Vector{IR.Type}, + then_branch::Region, + else_branch::Region, + location=Location(), +) + _results = IR.Type[output...,] + _operands = Value[cond, inputs...] + _owned_regions = Region[then_branch, else_branch] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.cond_if", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`int_div` + +Elementwise integer divide operator of input1 by input2. Axis of size 1 +will be broadcast, as necessary. +""" +function int_div(input1::Value, input2::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.int_div", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`log` + +Elementwise natural logarithm operation +""" +function log(input1::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.log", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`logical_and` + +Elementwise logical AND of input1 and input2. Axis of size 1 will be +broadcast, as necessary. +""" +function logical_and(input1::Value, input2::Value; z::IR.Type, location=Location()) + _results = IR.Type[z,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.logical_and", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`logical_left_shift` + +Elementwise left shift of input1 and input2. Axis of size 1 will be +broadcast, as necessary. +""" +function logical_left_shift( + input1::Value, input2::Value; output::IR.Type, location=Location() +) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.logical_left_shift", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`logical_not` + +Elementwise logical NOT of input. +""" +function logical_not(input1::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.logical_not", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`logical_or` + +Elementwise logical OR of input1 and input2. Axis of size 1 will be +broadcast as necessary. +""" +function logical_or(input1::Value, input2::Value; z::IR.Type, location=Location()) + _results = IR.Type[z,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.logical_or", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`logical_right_shift` + +Elementwise logical right shift of input1 by the amount specified in input2. +Axis of size 1 will be broadcast, as necessary. +""" +function logical_right_shift( + input1::Value, input2::Value; output::IR.Type, location=Location() +) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.logical_right_shift", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`logical_xor` + +Elementwise logical XOR of input1 and input2. Axis of size 1 will be +broadcast as necessary. +""" +function logical_xor(input1::Value, input2::Value; z::IR.Type, location=Location()) + _results = IR.Type[z,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.logical_xor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`matmul` + +Performs a two dimensional matrix multiplication. This allows both inputs to +be activations, rather than reserving weights as an attribute in the +FULLY_CONNECTED operator. +""" +function matmul( + a::Value, b::Value; c::IR.Type, quantization_info=nothing, location=Location() +) + _results = IR.Type[c,] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(quantization_info) && + push!(_attributes, namedattribute("quantization_info", quantization_info)) + + return IR.create_operation( + "tosa.matmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`max_pool2d` + +This performs a max pooling over the given input tensor. A sliding window of +size given by is passed over the input tensor, with the +maximum value being placed in the +output tensor. +""" +function max_pool2d(input::Value; output::IR.Type, kernel, stride, pad, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("kernel", kernel), + namedattribute("stride", stride), + namedattribute("pad", pad), + ] + + return IR.create_operation( + "tosa.max_pool2d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`maximum` + +Elementwise max of input1 and input2. Axis of size 1 will be broadcast, as +necessary. +""" +function maximum(input1::Value, input2::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.maximum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`minimum` + +Elementwise minimum of input1 and input2. Axis of size 1 +will be broadcast, as necessary. +""" +function minimum(input1::Value, input2::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.minimum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mul` + +Elementwise multiplication (Hadamard product) of input1 and input2. +Axis of size 1 will be broadcast, as necessary. +i8/i16 input type can be promoted to i32 result type. +""" +function mul(input1::Value, input2::Value; output::IR.Type, shift, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("shift", shift),] + + return IR.create_operation( + "tosa.mul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`negate` + +Elementwise negation operation +""" +function negate( + input1::Value; output::IR.Type, quantization_info=nothing, location=Location() +) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(quantization_info) && + push!(_attributes, namedattribute("quantization_info", quantization_info)) + + return IR.create_operation( + "tosa.negate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pad` + +The `tosa.pad` operation pads a tensor along borders of each dimension with +`pad_const` (defaults to zero), given a padding configuration `padding` +specifying low and high values along the dimensions. + +# Example + +```mlir +%0 = arith.constant dense<[[1, 2], [3, 4]]> : tensor<2x2xi32> +tosa.pad %arg0, %0 : (tensor<1x2xf32>, tensor<2x2xi32>) -> (tensor<4x9xf32>) +``` + +Example 2: + +```mlir +%0 = arith.constant dense<[[-1, 2], [3, 4]]> : tensor<2x2xi32> +tosa.pad %arg0, %0 : (tensor<1x2xf32>, tensor<2x2xi32>) -> (tensor) +``` +""" +function pad( + input1::Value, + padding::Value, + pad_const=nothing::Union{Nothing,Value}; + output::IR.Type, + quantization_info=nothing, + location=Location(), +) + _results = IR.Type[output,] + _operands = Value[input1, padding] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(pad_const) && push!(_operands, pad_const) + !isnothing(quantization_info) && + push!(_attributes, namedattribute("quantization_info", quantization_info)) + + return IR.create_operation( + "tosa.pad", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`pow` + +Elementwise input1 raised to the power of input2. +Axis of size 1 will be broadcast, as necessary. +""" +function pow(input1::Value, input2::Value; z::IR.Type, location=Location()) + _results = IR.Type[z,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.pow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`rfft2d` + +Performs a batched 2D real-valued Fast Fourier Transform over the input where +the input tensor consists of real values producing complex valued output. The +complex output values will be split into the output_real and output_imag +tensor arguments. RFFT2D takes advantage of Hermitian symmetry to only +calculate the first half of the final output axis. Imaginary values with +locations (0,0), (0,W/2), (H/2,0) and (H/2,W/2) are zero. + +# Example + +```mlir + %real, %imag = tosa.rfft2d %in : (tensor<8x16xf32>) -> (tensor<8x9xf32>, tensor<8x9xf32>) +``` +""" +function rfft2d( + input::Value; + output_real::IR.Type, + output_imag::IR.Type, + local_bound=nothing, + location=Location(), +) + _results = IR.Type[output_real, output_imag] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(local_bound) && + push!(_attributes, namedattribute("local_bound", local_bound)) + + return IR.create_operation( + "tosa.rfft2d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reciprocal` + +Elementwise reciprocal operation. For integer operation, a TABLE should be +used with the appropriate ranges. +""" +function reciprocal(input1::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.reciprocal", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reduce_all` + +Reduce a tensor along the given axis with a logical AND operation +""" +function reduce_all( + input::Value; output=nothing::Union{Nothing,IR.Type}, axis, location=Location() +) + _results = IR.Type[] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("axis", axis),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "tosa.reduce_all", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`reduce_any` + +Reduce a tensor along the given axis with a logical OR operation +""" +function reduce_any( + input::Value; output=nothing::Union{Nothing,IR.Type}, axis, location=Location() +) + _results = IR.Type[] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("axis", axis),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "tosa.reduce_any", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`reduce_max` + +Reduce a tensor along the given axis with a maximum operation +""" +function reduce_max( + input::Value; output=nothing::Union{Nothing,IR.Type}, axis, location=Location() +) + _results = IR.Type[] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("axis", axis),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "tosa.reduce_max", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`reduce_min` + +Reduce a tensor along the given axis with a minimum operation +""" +function reduce_min( + input::Value; output=nothing::Union{Nothing,IR.Type}, axis, location=Location() +) + _results = IR.Type[] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("axis", axis),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "tosa.reduce_min", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`reduce_prod` + +Reduce a tensor along the given axis by computing the product of the axis. +""" +function reduce_prod( + input::Value; output=nothing::Union{Nothing,IR.Type}, axis, location=Location() +) + _results = IR.Type[] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("axis", axis),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "tosa.reduce_prod", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`reduce_sum` + +Reduce a tensor along the given axis by computing the sum of the axis. +""" +function reduce_sum( + input::Value; output=nothing::Union{Nothing,IR.Type}, axis, location=Location() +) + _results = IR.Type[] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("axis", axis),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "tosa.reduce_sum", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`rescale` + +Rescale quantized values into a new domain. Supported rescalings are: +Mode Input Output +signed 8 to 8 int8 int8 +signed 8 to 16 int8 int16 +signed 8 to 32 int8 int32 +signed 16 to 8 int16 int8 +signed 16 to 16 int16 int16 +signed 16 to 32 int16 int32 +signed 32 to 8 int32 int8 +signed 32 to 16 int32 int16 +signed 32 to 32 int32 int32 +signed 48 to 8 int48 int8 +signed 48 to 16 int48 int16 +signed 48 to 32 int48 int32 +unsigned 8 to signed 8 uint8 int8 +signed 8 to unsigned 8 int8 uint8 +""" +function rescale( + input::Value; + output::IR.Type, + input_zp, + output_zp, + multiplier, + shift, + scale32, + double_round, + per_channel, + location=Location(), +) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("input_zp", input_zp), + namedattribute("output_zp", output_zp), + namedattribute("multiplier", multiplier), + namedattribute("shift", shift), + namedattribute("scale32", scale32), + namedattribute("double_round", double_round), + namedattribute("per_channel", per_channel), + ] + + return IR.create_operation( + "tosa.rescale", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reshape` + +Returns a tensor with the same type/values as the input, with a new shape +specified by the shape argument. Reshape may operate on tensors of any rank. +No data conversion happens during a reshape operation. +""" +function reshape( + input1::Value; output=nothing::Union{Nothing,IR.Type}, new_shape, location=Location() +) + _results = IR.Type[] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("new_shape", new_shape),] + !isnothing(output) && push!(_results, output) + + return IR.create_operation( + "tosa.reshape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`resize` + +Resizes a tensor. Resize is only allowed in the H and W dimensions. In +expected use, The height dimension is scaled by factor (scale_y_n/scale_y_d). +And the width dimension is scaled by factor (scale_x_n/scale_x_d). Thus the +output dimensions can be derived from the input dimensions by inverting the +scale. And the [order_y, border_x] values adjust the output size to allow +fractional sampling beyond integer input position (IH-1,IW-1). +""" +function resize( + input::Value; output::IR.Type, scale, offset, border, mode, location=Location() +) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("scale", scale), + namedattribute("offset", offset), + namedattribute("border", border), + namedattribute("mode", mode), + ] + + return IR.create_operation( + "tosa.resize", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reverse` + +Returns a tensor with the same type/values as the input, with the data +reversed along the given axis. No data conversion happens during a reverse +operation. +""" +function reverse(input::Value; output::IR.Type, axis, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("axis", axis),] + + return IR.create_operation( + "tosa.reverse", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`rsqrt` + +Elementwise reciprocal square root operation. For integer operation, a TABLE +should be used with the appropriate ranges. +""" +function rsqrt(input1::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.rsqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`scatter` + +The values_out tensor is set to the values_in tensor with data modified as follows: +data from the input tensor is inserted at the positions specified by the indices tensor. +""" +function scatter( + values_in::Value, indices::Value, input::Value; values_out::IR.Type, location=Location() +) + _results = IR.Type[values_out,] + _operands = Value[values_in, indices, input] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.scatter", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`select` + +Elementwise select of the output based on a condition. +""" +function select( + pred::Value, on_true::Value, on_false::Value; output::IR.Type, location=Location() +) + _results = IR.Type[output,] + _operands = Value[pred, on_true, on_false] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.select", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sigmoid` + +Sigmoid function: output = 1 / (1 + exp(-input)) +For quantized integer data types, the TABLE operator should be used instead +with the following definition. The sigmoid table has 513 entries each of +16-bit precision and covering the input range -16.0 to +16.0 +in steps of 1/16. +""" +function sigmoid(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.sigmoid", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sin` + +Elementwise sine operation for values given in radians. +""" +function sin(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.sin", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`slice` + +Extracts a slice of the input1 on the given axis, beginning at the +start coordinates, and extending for size elements in each direction. No +data conversion happens during a slice operation. +""" +function slice(input::Value; output::IR.Type, start, size, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("start", start), namedattribute("size", size) + ] + + return IR.create_operation( + "tosa.slice", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sub` + +Elementwise subtraction of input1 and input2. Axis of size 1 will be +broadcast as necessary. +""" +function sub(input1::Value, input2::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, input2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.sub", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`table` + +Interpolated table lookup operation. Input values are scaled to create a +fixed-point 9.7 value. The high 9 bits are used to index into the table. +The fractional bits are used to interpolate based on the looked up value and +the index+1 value in the table. The TABLE operator then returns a 16.7 +interpolated value. Note that there must be 513 values to handle the full +range of inputs. + +The TABLE operator is expected to be used as follows: +* A RESCALE node is expected before the TABLE operator to scale the input + to a full int16_t range for the table lookup +* If an int16_t result is required then follow the TABLE operator with a + RESCALE with a right shift of 7 +* If an int8_t result is required then follow the TABLE operator with a + RESCALE with a right shift of 15 +""" +function table(input::Value, table::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input, table] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.table", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tanh` + +Parameterized hyperbolic tangent. +For quantized integer data types, the TABLE operator should be used instead +with the following definition. The tanh_table has 513 entries each of +16-bit precision and covering the input range -8.0 to +8.0 in steps of 1/32. +""" +function tanh(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.tanh", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tile` + +Replicates input 0 multiplies times along each dimension. +""" +function tile(input1::Value; output::IR.Type, multiples, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("multiples", multiples),] + + return IR.create_operation( + "tosa.tile", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`transpose_conv2d` + +Performs a 2D transposed convolution over the given tensor input, using the +weights tensor. +""" +function transpose_conv2d( + input::Value, + filter::Value, + bias::Value; + output::IR.Type, + out_pad, + stride, + out_shape, + quantization_info=nothing, + local_bound=nothing, + location=Location(), +) + _results = IR.Type[output,] + _operands = Value[input, filter, bias] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("out_pad", out_pad), + namedattribute("stride", stride), + namedattribute("out_shape", out_shape), + ] + !isnothing(quantization_info) && + push!(_attributes, namedattribute("quantization_info", quantization_info)) + !isnothing(local_bound) && + push!(_attributes, namedattribute("local_bound", local_bound)) + + return IR.create_operation( + "tosa.transpose_conv2d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`transpose` + +Permutes the dimensions based on perm. +""" +function transpose(input1::Value, perms::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input1, perms] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.transpose", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`variable` + +Defines a new TOSA variable. This is a mutable value. +Modifications are expressed using read/write semantics. +""" +function variable(; name, type, initial_value=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("name", name), namedattribute("type", type)] + !isnothing(initial_value) && + push!(_attributes, namedattribute("initial_value", initial_value)) + + return IR.create_operation( + "tosa.variable", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`variable_read` + +Reads the value from a pseudo-buffer resource holding a mutable tensor. +""" +function variable_read(; value::IR.Type, name, location=Location()) + _results = IR.Type[value,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("name", name),] + + return IR.create_operation( + "tosa.variable.read", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`variable_write` + +Assigns a value to pseudo-buffer resource holding a mutable tensor. +""" +function variable_write(value::Value; name, location=Location()) + _results = IR.Type[] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("name", name),] + + return IR.create_operation( + "tosa.variable.write", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`while_loop` + +Generates and evaluates a Bool condition and either executes a loop body or +exits to another control point. This action is performed repeatedly after +updating and re-evaluating the Boolean condition every iteration. This +implements the semantic foreach or while iterative loop structure. +""" +function while_loop( + inputs::Vector{Value}; + output::Vector{IR.Type}, + cond::Region, + body::Region, + location=Location(), +) + _results = IR.Type[output...,] + _operands = Value[inputs...,] + _owned_regions = Region[cond, body] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.while_loop", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +return operation within the conditional and body of +structured control flow. Operation takes variadic operands +but produces no results of its own. +""" +function yield(inputs::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[inputs...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "tosa.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # tosa diff --git a/src/Dialects/19/Transform.jl b/src/Dialects/19/Transform.jl new file mode 100644 index 00000000..2d4d1505 --- /dev/null +++ b/src/Dialects/19/Transform.jl @@ -0,0 +1,8271 @@ +module transform + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`affine_simplify_bounded_affine_ops` + +Simplify the targeted affine.min / affine.max ops given the supplied +lower and upper bounds for values that may be used as target op operands. + +# Example +``` +%0 = transform.structured.match ops{[\"affine.min\", \"affine.max\"]} in %arg1 +%1 = transform.structured.match ops{[\"gpu.lane_id\"]} in %arg1 +transform.affine.simplify_bounded_affine_ops %0 with [%1] within [0] and [32] + +// Multiple bounds can be specified. +transform.affine.simplify_bounded_affine_ops %0 with [%1, %2] within [0, 5] and [32, 50] +``` + +Bounded op handles (`%1` and `%2) must be mapped to ops that have a single +result of index type. The sets of target ops and bounded ops must not +overlap. + +#### Return modes + +Target ops must be affine.min or affine.max ops. This transform consumes the +target handle and does not produce any handle. It reads the bounded op +handles. + +TODO: Support affine.apply targets. +TODO: Allow mixed PDL_Operation/int64_t for lower_bounds and upper_bounds. +""" +function affine_simplify_bounded_affine_ops( + target::Value, + bounded_values::Vector{Value}; + lower_bounds, + upper_bounds, + location=Location(), +) + _results = IR.Type[] + _operands = Value[target, bounded_values...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("lower_bounds", lower_bounds), + namedattribute("upper_bounds", upper_bounds), + ] + + return IR.create_operation( + "transform.affine.simplify_bounded_affine_ops", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`bufferization_buffer_loop_hoisting` + +Hoist buffer allocations (\"memref.alloc\" and \"memref.alloca\") from loops +within the targeted op. This transform assumes that there are no buffer +deallocation ops in the IR. + +This transform reads the `target` handle and modifies the payload. +""" +function bufferization_buffer_loop_hoisting(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.bufferization.buffer_loop_hoisting", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bufferization_eliminate_empty_tensors` + +Try to eliminate all `tensor.empty` ops within the targeted op by replacing +them with another destination tensor. + +\"tensor.empty\" ops cannot be bufferized. They can either be converted to +\"bufferization.alloc_tensor\" or replaced with another tensor (via this +transform). \"tensor.empty\" does not specify the contents of the returned +tensor so their results can be replaced with arbitrary tensor values as long +as the dimensions match. + +This transformation looks for subset ops that insert a tensor that +originates from a \"tensor.empty\" (as per the reverse use-def chain). Such +\"tensor.empty\" ops are replaced with the destination subset. + +# Example + +``` +%0 = tensor.empty() : tensor<5xf32> +%1 = linalg.fill ... outs(%0) +%2 = tensor.insert_slice %1 into %t[1][5][1] +``` + +Is rewritten with: +``` +%0 = tensor.extract_slice %t[1][5][1] +%1 = linalg.fill ... outs(%0) +%2 = tensor.insert_slice %1 into %t[1][5][1] +``` + +In the above example, the subset op is \"tensor.insert_slice\". When tracing +back the reverse use-def chain of a the source, we end up at a +\"tensor.empty\" op. + +The above example can bufferize without an allocation (in the absence of +other conflicts) because there is no longer a `tensor.empty` op. + +See `-eliminate-empty-tensors` for more details. + +#### Return modes + +This transform reads the target handle and modifies the payload. It does +not produce any handle. +""" +function bufferization_eliminate_empty_tensors(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.bufferization.eliminate_empty_tensors", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bufferization_empty_tensor_to_alloc_tensor` + +Replace a tensor.empty with a bufferization.tensor_alloc. + +#### Return modes + +This operation consumes the `target` handle and produces the `transformed` +handle. `target` is expected to be a `tensor.empty` operation. The transform +always succeeds. +""" +function bufferization_empty_tensor_to_alloc_tensor( + target::Value; transformed::IR.Type, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.bufferization.empty_tensor_to_alloc_tensor", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`bufferization_one_shot_bufferize` + +Indicates that the given `target` op should be bufferized with One-Shot +Bufferize. The bufferization can be configured with various attributes that +corresponding to options in `BufferizationOptions` and the +`one-shot-bufferize` pass. More information can be found in the pass +documentation. + +The targeted ops must be modules or functions. This is because there is +always a single, bufferized replacement op for such targets. + +Note: Only ops that implement `BufferizableOpInterface` are bufferized. All +other ops are ignored if `allow_unknown_ops`. If `allow_unknown_ops` is +unset, this transform fails when an unknown/non-bufferizable op is found. +Many ops implement `BufferizableOpInterface` via an external model. These +external models must be registered when applying this transform op; +otherwise, said ops would be considered non-bufferizable. + +#### Return modes + +This operation consumes the `target` handle and produces the `transformed` +handle. +""" +function bufferization_one_shot_bufferize( + target::Value; + transformed::IR.Type, + function_boundary_type_conversion=nothing, + allow_return_allocs_from_loops=nothing, + allow_unknown_ops=nothing, + bufferize_function_boundaries=nothing, + dump_alias_sets=nothing, + test_analysis_only=nothing, + print_conflicts=nothing, + check_parallel_regions=nothing, + memcpy_op=nothing, + location=Location(), +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(function_boundary_type_conversion) && push!( + _attributes, + namedattribute( + "function_boundary_type_conversion", function_boundary_type_conversion + ), + ) + !isnothing(allow_return_allocs_from_loops) && push!( + _attributes, + namedattribute("allow_return_allocs_from_loops", allow_return_allocs_from_loops), + ) + !isnothing(allow_unknown_ops) && + push!(_attributes, namedattribute("allow_unknown_ops", allow_unknown_ops)) + !isnothing(bufferize_function_boundaries) && push!( + _attributes, + namedattribute("bufferize_function_boundaries", bufferize_function_boundaries), + ) + !isnothing(dump_alias_sets) && + push!(_attributes, namedattribute("dump_alias_sets", dump_alias_sets)) + !isnothing(test_analysis_only) && + push!(_attributes, namedattribute("test_analysis_only", test_analysis_only)) + !isnothing(print_conflicts) && + push!(_attributes, namedattribute("print_conflicts", print_conflicts)) + !isnothing(check_parallel_regions) && + push!(_attributes, namedattribute("check_parallel_regions", check_parallel_regions)) + !isnothing(memcpy_op) && push!(_attributes, namedattribute("memcpy_op", memcpy_op)) + + return IR.create_operation( + "transform.bufferization.one_shot_bufferize", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`apply_conversion_patterns_func_func_to_llvm` + +Collects patterns that convert Func dialect ops to LLVM dialect ops. +These patterns require an \"LLVMTypeConverter\". +""" +function apply_conversion_patterns_func_func_to_llvm(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_conversion_patterns.func.func_to_llvm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`func_cast_and_call` + +This transform takes value handles to a set of `inputs` and `outputs` and +attempts to cast them to the function signature of the attached function +op, then builds a call to the function and replaces the users of the +outputs. It is the responsibility of the user to ensure that the slice of +the program replaced by this operation makes sense, i.e. there is no +verification that the inputs to this operation have any relation to the +outputs outside of basic dominance requirements needed for the call. + +The casting materialization functions are specified in the graph region of +this op. They must implement the `TypeConverterBuilderOpInterface`. The +order of ops within the region is irrelevant. + +The target function can be specified by a symbol name or by a handle to the +operation. + +This transform only reads the operand handles and only replaces the users of +the outputs with the results of the call. No handles are consumed and no +operations are removed. Users are expected to run cleanup separately if +desired. + +Warning: The replacement of the uses of the outputs could invalidate certain +restricted value handle types (e.g. `transform.block_arg` if it existed, by +replacing the use with something not coming from a block argument). The +value will still exist in such cases but wouldn\'t verify against the type. +See the discussion here for more information: +https://github.com/llvm/llvm-project/pull/78398#discussion_r1455070087 + +This transform will emit a silenceable failure if: + - The set of outputs isn\'t unique + - The handle for the insertion point does not include exactly one operation + - The insertion point op does not dominate any of the output users + - The insertion point op is not dominated by any of the inputs + - The function signature does not match the number of inputs/outputs + +This transform will emit a definite failure if it fails to resolve the +target function, or if it fails to materialize the conversion casts of +either the inputs to the function argument types, or the call results to +the output types. +""" +function func_cast_and_call( + insertion_point::Value, + inputs=nothing::Union{Nothing,Value}; + outputs=nothing::Union{Nothing,Value}, + function_=nothing::Union{Nothing,Value}, + result::IR.Type, + insert_after=nothing, + function_name=nothing, + conversions::Region, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[insertion_point,] + _owned_regions = Region[conversions,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(inputs) && push!(_operands, inputs) + !isnothing(outputs) && push!(_operands, outputs) + !isnothing(function_) && push!(_operands, function_) + push!( + _attributes, + operandsegmentsizes([ + 1, + isnothing(inputs) ? 0 : 1, + isnothing(outputs) ? 0 : 1, + isnothing(function_) ? 0 : 1, + ]), + ) + !isnothing(insert_after) && + push!(_attributes, namedattribute("insert_after", insert_after)) + !isnothing(function_name) && + push!(_attributes, namedattribute("function_name", function_name)) + + return IR.create_operation( + "transform.func.cast_and_call", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`apply_patterns_gpu_gpu_rewrite_patterns` + +Collects GPU rewrite patterns comprising: + 1. GpuAllReduceRewrite patterns + 2. GpuGlobalIdRewriter patterns + 3. GpuShuffleRewriter patterns +""" +function apply_patterns_gpu_gpu_rewrite_patterns(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.gpu.gpu_rewrite_patterns", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_conversion_patterns_gpu_gpu_subgroup_reduce_to_nvvm` + +Collects patterns that convert GPU dialect ops related to wmma ops +to NVVM dialect ops. +These patterns require an \"LLVMTypeConverter\". +""" +function apply_conversion_patterns_gpu_gpu_subgroup_reduce_to_nvvm(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_conversion_patterns.gpu.gpu_subgroup_reduce_to_nvvm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_conversion_patterns_gpu_gpu_to_nvvm` + +Collects patterns that convert GPU dialect ops to NVVM dialect ops. These +patterns require an \"LLVMTypeConverter\". +""" +function apply_conversion_patterns_gpu_gpu_to_nvvm(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_conversion_patterns.gpu.gpu_to_nvvm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_conversion_patterns_gpu_gpu_wmma_to_nvvm` + +Collects patterns that convert GPU dialect ops related to wmma ops +to NVVM dialect ops. +These patterns require an \"LLVMTypeConverter\". +""" +function apply_conversion_patterns_gpu_gpu_wmma_to_nvvm(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_conversion_patterns.gpu.gpu_wmma_to_nvvm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_gpu_unroll_vectors_subgroup_mma` + +Unrolls contractions to the target `m`, `n`, and `k` native vector size, +along with other vector operations based on expected usage. `transfer_read` +ops unroll based on the extract slice shape introduced by unrolling the +contractions, while elementwise and `transfer_write` ops unroll to the shape of +the C matrix (`m x n`). + +This operation applies to pure vector operations and should be applied before +lowering to subgroup_mma ops. +""" +function apply_patterns_gpu_unroll_vectors_subgroup_mma(; m, n, k, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("m", m), namedattribute("n", n), namedattribute("k", k) + ] + + return IR.create_operation( + "transform.apply_patterns.gpu.unroll_vectors_subgroup_mma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_gpu_eliminate_barriers` + +Removes unnecessary GPU barriers from the function. If a barrier does not +enforce any conflicting pair of memory effects, including a pair that is +enforced by another barrier, it is unnecessary and can be removed. + +The approach is based on \"High-Performance GPU-to-CPU Transpilation and +Optimization via High-Level Parallel Constructs\" by Moses, Ivanov, +Domke, Endo, Doerfert, and Zinenko in PPoPP 2023. Specifically, it +analyzes the memory effects of the operations before and after the given +barrier and checks if the barrier enforces any of the memory +effect-induced dependencies that aren\'t already enforced by another +barrier. + +For example, in the following code + +```mlir + store %A + barrier // enforces load-after-store + load %A + barrier // load-after-store already enforced by the previous barrier + load %A +``` + +the second barrier can be removed. +""" +function apply_patterns_gpu_eliminate_barriers(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.gpu.eliminate_barriers", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`gpu_map_forall_to_blocks` + +Target the gpu_launch op and rewrite the top level `scf.forall` +to distributed gpu.block_id attribute. If `generate_gpu_launch` attribute +is set, then first generates `gpu_launch` and moves the top level +`scf.forall` inside. + +The operation searches top level `scf.forall` ops under +`gpu_launch` and maps each such op to GPU blocks. Mapping is +one-to-one and the induction variables of `scf.forall` are +rewritten to gpu.block_id according to the `thread_dim_mapping` attribute. + +Dynamic, `scf.forall` trip counts are currently not supported. +Dynamic block dim sizes are currently not supported. + +Only **bufferized** scf.forall are currently supported. +Only scf.forall distributed to **at most 3 dimensions** are +currently supported. + +The operation alters the block size of the given gpu_launch using the +grid_dims argument. + +#### Return modes: + +This operation ignores non-gpu_launch ops and drops them in the return. + +If any scf.forall with tensors is found, the transform definitely +fails. + +If all the scf.forall operations contained within the LaunchOp +referred to by the `target` PDLOperation lower to GPU properly, the +transform succeeds. Otherwise the transform definitely fails. + +The returned handle points to the same LaunchOp operand, consuming it and +producing a new SSA value to satisfy chaining and linearity of the IR +properties. +""" +function gpu_map_forall_to_blocks( + target::Value; + result::IR.Type, + grid_dims=nothing, + generate_gpu_launch=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(grid_dims) && push!(_attributes, namedattribute("grid_dims", grid_dims)) + !isnothing(generate_gpu_launch) && + push!(_attributes, namedattribute("generate_gpu_launch", generate_gpu_launch)) + + return IR.create_operation( + "transform.gpu.map_forall_to_blocks", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`gpu_map_nested_forall_to_threads` + +Target the `gpu.launch op` and rewrite all `scf.forall` nested in it to +distributed `gpu.thread_id` attribute. + +The operation searches for `scf.forall` ops nested under `target` and maps +each such op to GPU threads. + +`scf.forall` induction variables are rewritten to `gpu.thread_id` according +to the `mapping` attribute. + +Different types of mappings attributes are supported: + - the block_dims is a list of integers that specifies the number of + threads in each dimension. This is a mandatory attribute that is used + to constrain the number of threads in each dimension. If an + `scf.forall` op is mapped to fewer threads, predication occurs. + - the warp_dims is a list of integers that specifies the number of + warps in each dimension. This is an optional attribute that is used + to constrain the number of warps in each dimension. When present, this + attribute must be specified in a way that is compatible with the + block_dims attribute. If an `scf.forall` op is mapped to fewer warps, + predication occurs. + +Dynamic `scf.forall` trip counts are currently not supported. +Dynamic block dim sizes are currently not supported. + +Only **bufferized** `scf.forall` are currently supported. +Only `scf.forall` distributed to **at most 3 dimensions** are +currently supported. + +The `sync_after_distribute`attribute controls whether a `gpu.barrier` is +inserted after each scf.forall op. At this time, this is an all or nothing +choice. This will need to be tightened in the future. + +The operation alters the block size of the given gpu_launch using the +mandatory block_dims argument. + +#### Return modes: + +This operation ignores non-gpu_launch ops and drops them in the return. + +If any scf.forall with tensors is found, the transform definitely +fails. + +If all the scf.forall operations with gpu.thread mapping contained +within the LaunchOp referred to by the `target` PDLOperation lower to GPU +properly, the transform succeeds. Otherwise the transform definitely +fails. + +scf.forall operations with mappings other than gpu.thread are +ignored. + +The returned handle points to the same LaunchOp operand, consuming it and +producing a new SSA value to satisfy chaining and linearity of the IR +properties. + +#### Example: + +``` +gpu.launch blocks(%bx, %by, %bz) in (%x = %0, %y = %1, %z = %2) + threads(%tx, %ty, %tz) in (%tx = %3, %ty = %4, %tz = %5) { + scf.forall (%i, %j) in (7, 9) { + ... // body 1 + } {mapping = [#gpu.thread, #gpu.thread, #gpu.thread]} + scf.forall (%i) in (12) { + ... // body 2 + } {mapping = [#gpu.thread]} + gpu.terminator +} +``` + +is translated to: + +``` +%bdimX = arith.constant 12 : index +%bdimY = arith.constant 9 : index +gpu.launch blocks(%bx, %by, %bz) in (%x = %0, %y = %1, %z = %2) + threads(%tx, %ty, %tz) in (%tx = %bdimX, %ty = %bdimY, %tz = %5) { + if (threadIdx.x < 9 && threadIdx.y < 7) { + ... // body 1 + } + gpu.barrier + if (threadIdx.y < 1) { + ... // body 2 + } + gpu.barrier + gpu.terminator +} +``` +""" +function gpu_map_nested_forall_to_threads( + target::Value; + result::IR.Type, + block_dims=nothing, + sync_after_distribute=nothing, + warp_size=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(block_dims) && push!(_attributes, namedattribute("block_dims", block_dims)) + !isnothing(sync_after_distribute) && + push!(_attributes, namedattribute("sync_after_distribute", sync_after_distribute)) + !isnothing(warp_size) && push!(_attributes, namedattribute("warp_size", warp_size)) + + return IR.create_operation( + "transform.gpu.map_nested_forall_to_threads", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`match_structured_body` + +Checks if the body of the structured payload op satisfies one of the +following mutually exclusive criteria specified by attributes: + + * `reduction_position`: the body of the structured payload op implements + a reduction of the `n`-th operand (`n` is the value of the attribute) + using a single combiner operation; + + * `passthrough`: the body of the structured payload op only forwards + inputs to the outputs (copy or broadcast). + + * `elementwise`: the body of the structured payload op represents an + elementwise operation. + + * `contraction`: the body of the structured payload op is a contraction + of the form `((bbarg0, bbarg1), bbarg2)` where `` and + `` are binary operations whose names are specified in the attribute + and operands can be permuted and optionally forwarded through a chain of + unary side effect-free operations. + + +This op can only appear immediately inside a `transform.match.structured` +op and apply to its first block argument because it assumes the payload +to have been already checked for being a single structured op. + + +#### Return modes + +Succeeds if the operation body satisfies the specified criteria, produces a +silenceable failure otherwise. Produces a definite failure if the operand is +not associated with a single payload op. +""" +function match_structured_body( + operand_handle::Value; + reduction_position=nothing, + passthrough=nothing, + elementwise=nothing, + contraction=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(reduction_position) && + push!(_attributes, namedattribute("reduction_position", reduction_position)) + !isnothing(passthrough) && + push!(_attributes, namedattribute("passthrough", passthrough)) + !isnothing(elementwise) && + push!(_attributes, namedattribute("elementwise", elementwise)) + !isnothing(contraction) && + push!(_attributes, namedattribute("contraction", contraction)) + + return IR.create_operation( + "transform.match.structured.body", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured_classify_contraction_dims` + +Checks if the structured payload op has contraction-like dimensions as +follows: + + C(batch, m, n) += A(batch, m, k) * B(batch, k, n) + +That is: + + - \'batch\' are parallel dimensions used in inputs and result; + - \'m\' are parallel dimensions used in the LHS and result; + - \'n\' are parallel dimensions used in rhe RHS and result; + - \'k\' are reduction dimensions present only in LHS and RHS. + +Note that this doesn\'t check the operation in the body. + + +This op can only appear immediately inside a `transform.match.structured` +op and apply to its first block argument because it assumes the payload +to have been already checked for being a single structured op. + + +#### Return modes + +Succeeds if the operation has the contraction-like dimensions, produces a +silenceable failure otherwise. +""" +function match_structured_classify_contraction_dims( + operand_handle::Value; + batch::IR.Type, + m::IR.Type, + n::IR.Type, + k::IR.Type, + location=Location(), +) + _results = IR.Type[batch, m, n, k] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.match.structured.classify_contraction_dims", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured_classify_convolution_dims` + +Checks if the structured payload op has convolution-like dimensions as +follows: + + C(batch, depth, oi, oc) += A(batch, depth, oi, ic) * B(fl, depth, ic, oc) + +That is: + + - \'batch\' are parallel dimensions used in the input and result; + - \'output_image\' (\'oi\') are parallel dimensions used in the input and result; + - \'output_channel\' (\'oc\') are parallel dimensions used in the filter and result; + - \'filter_loop\' (\'fl\') are reduction dimensions representing the dimensions of the sliding window; + - \'input_channel\' (\'ic\') are reduction dimensions present only in the input and filter. + - \'depth\' (\'ic\') are parallel dimensions present in the input, filter, and output. + +Additionally this will match stride and dilation information for the convolution: + - \'strides\' are the static strides per convolution window dimension; + - \'dilations\' are the static dilations per convolution window dimension. + +Note that this doesn\'t check the operation in the body. + + +This op can only appear immediately inside a `transform.match.structured` +op and apply to its first block argument because it assumes the payload +to have been already checked for being a single structured op. + + +#### Return modes + +Succeeds if the operation has the convolution-like dimensions, produces a +silenceable failure otherwise. +""" +function match_structured_classify_convolution_dims( + operand_handle::Value; + batch::IR.Type, + output_image::IR.Type, + output_channel::IR.Type, + filter_loop::IR.Type, + input_channel::IR.Type, + depth::IR.Type, + strides::IR.Type, + dilations::IR.Type, + location=Location(), +) + _results = IR.Type[ + batch, + output_image, + output_channel, + filter_loop, + input_channel, + depth, + strides, + dilations, + ] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.match.structured.classify_convolution_dims", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured_dim` + +Checks if the dimensions (loop ranges) of the structured payload op satisfy +the criteria specified as attributes. May capture the numeric value of the +dimension into a parameter that it returns. + + + The following dimension specifications are supported: + + * `all`: all dimensions are checked and captured; + * list of integers: the listed dimensions are checked and captured; + * `except(` list of integers `)`: all dimensions except the + specified ones are checked and captured. + +Negative indexes are interpreted by counting values from the last one +(similarly to Python). For example, `-1` means the last dimension and +`except(-1)` means all dimensions but the last. Indexes must be unique, +including after interpretation of negative ones. + +Produces a silenceable failure in case of index overflow, including backward +counting. + + +The following mutually exclusive conditions are available as unit +attributes: + + * `parallel`: the dimension corresponds to a parallel loop; + * `reduction`: the dimension corresponds to a reduction loop. + +If the result type is specified, associates the parameter with the (static) +values of dimensions in the same order as listed and preserving the natural +order for `all` and `except`. Specifically, if `-1, -2` are specified, the +parameter will be associated with the value of the second-to-last dimension +followed by the last dimension. If the dimension is dynamic, the parameter +will contain a negative value corresponding to kDynamic in C++. + + +This op can only appear immediately inside a `transform.match.structured` +op and apply to its first block argument because it assumes the payload +to have been already checked for being a single structured op. + + +#### Return modes + +Succeeds if the specified dimensions satisfy the specified criteria, +produces a silenceable failure otherwise. Produces a definite failure if +the operand is not associated with a single payload op. +""" +function match_structured_dim( + operand_handle::Value; + result=nothing::Union{Nothing,IR.Type}, + raw_dim_list, + is_inverted=nothing, + is_all=nothing, + parallel=nothing, + reduction=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("raw_dim_list", raw_dim_list),] + !isnothing(result) && push!(_results, result) + !isnothing(is_inverted) && + push!(_attributes, namedattribute("is_inverted", is_inverted)) + !isnothing(is_all) && push!(_attributes, namedattribute("is_all", is_all)) + !isnothing(parallel) && push!(_attributes, namedattribute("parallel", parallel)) + !isnothing(reduction) && push!(_attributes, namedattribute("reduction", reduction)) + + return IR.create_operation( + "transform.match.structured.dim", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured_elemental_bitwidth` + +Produces a transform dialect parameter associated with the bitwidth of the +elemental type of the payload value passed as the operand. +This op can only appear immediately inside a `transform.match.structured` +op and apply to its first block argument because it assumes the payload +to have been already checked for being a single structured op. + + +#### Return modes + +Succeeds if the operand is associated with exactly one payload value of +`ShapedType`. Produces a silenceable failure otherwise. +""" +function match_structured_elemental_bitwidth( + operand_handle::Value; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.match.structured.elemental_bitwidth", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured_init` + +Produces a transform dialect value depending on the result type: + - If the result type is a value handle, it will be associated with the init + operand(s) of the payload operation associated with the operand handle. + - If the result type is an operation handle, it will be associated with the + operation defining the init operand(s) of the payload operation associated + with the operand handle. + - If the result type is an affine map parameter type, it will be associated + with the indexing map that corresponds to the init operand(s) of the + payload operation associated with the operand handle. + +For example, given the following operation: + +```mlir +%arg3 = linalg.fill +linalg.matmul ins(%arg1, %arg2 : ...) outs(%arg3 : ...) +``` + +in case of a successful match for init operand 0 this operation will return, +for each of the respective cases above: + + - A handle to `%arg3` if the result is a value handle. + - A handle to `linalg.fill` if the result is an operation handle. + - A parameter containing the result map of the matrix multiplication, i.e. + `affine_map<(d0, d1, d2) -> (d0, d1)>` if the result is an affine + map parameter. + +The match succeeds if the conditions specified as attributes succeed. + + + The following init specifications are supported: + + * `all`: all inits are checked and captured; + * list of integers: the listed inits are checked and captured; + * `except(` list of integers `)`: all inits except the + specified ones are checked and captured. + +Negative indexes are interpreted by counting values from the last one +(similarly to Python). For example, `-1` means the last init and +`except(-1)` means all inits but the last. Indexes must be unique, +including after interpretation of negative ones. + +Produces a silenceable failure in case of index overflow, including backward +counting. + + + +This op can only appear immediately inside a `transform.match.structured` +op and apply to its first block argument because it assumes the payload +to have been already checked for being a single structured op. + + +#### Return modes + +Succeeds if all init(outs) indexes are in bounds, produces a silenceable +failure otherwise. Additionally, when the result is an operation handle, +produces a silenceable failure if the init(outs) specification defines +more than one init(outs) or if the operand is not an operation result. +""" +function match_structured_init( + operand_handle::Value; + result=nothing::Union{Nothing,IR.Type}, + raw_position_list, + is_inverted=nothing, + is_all=nothing, + permutation=nothing, + projected_permutation=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("raw_position_list", raw_position_list),] + !isnothing(result) && push!(_results, result) + !isnothing(is_inverted) && + push!(_attributes, namedattribute("is_inverted", is_inverted)) + !isnothing(is_all) && push!(_attributes, namedattribute("is_all", is_all)) + !isnothing(permutation) && + push!(_attributes, namedattribute("permutation", permutation)) + !isnothing(projected_permutation) && + push!(_attributes, namedattribute("projected_permutation", projected_permutation)) + + return IR.create_operation( + "transform.match.structured.init", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured_input` + +Produces a transform dialect value depending on the result type: + + - If the result type is a value handle, it will be associated with the input + operand(s) of the payload operation associated with the operand handle. + - If the result type is an operation handle, it will be associated with the + operation defining the input operand(s) of the payload operation associated + with the operand handle. + - If the result type is an affine map parameter type, it will be associated + with the indexing map that corresponds to the input operand(s) of the + payload operation associated with the operand handle. + +For example, given the following operation: + +```mlir +%arg1 = some.op +linalg.matmul ins(%arg1, %arg2 : ...) outs(%arg3 : ...) +``` + +in case of a successful match for operand 0 this operation will return, for +each of the respective cases above: + + - A handle to `%arg1` if the result is a value handle. + - A handle to `some.op` if the result is an operation handle. + - A parameter containing the LHS map of the matrix multiplication, i.e. + `affine_map<(d0, d1, d2) -> (d0, d2)>` if the result is an affine + map parameter. + +The match succeeds if the conditions specified as attributes succeed. + + + The following input specifications are supported: + + * `all`: all inputs are checked and captured; + * list of integers: the listed inputs are checked and captured; + * `except(` list of integers `)`: all inputs except the + specified ones are checked and captured. + +Negative indexes are interpreted by counting values from the last one +(similarly to Python). For example, `-1` means the last input and +`except(-1)` means all inputs but the last. Indexes must be unique, +including after interpretation of negative ones. + +Produces a silenceable failure in case of index overflow, including backward +counting. + + + +This op can only appear immediately inside a `transform.match.structured` +op and apply to its first block argument because it assumes the payload +to have been already checked for being a single structured op. + + +#### Return modes + +Succeeds if all input indexes are in bounds, produces a silenceable failure +otherwise. Additionally, when the result is an operation handle, produces a +silenceable failure if the input specification defines more than one input +or if the operand is not an operation result. +""" +function match_structured_input( + operand_handle::Value; + result=nothing::Union{Nothing,IR.Type}, + raw_position_list, + is_inverted=nothing, + is_all=nothing, + permutation=nothing, + projected_permutation=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("raw_position_list", raw_position_list),] + !isnothing(result) && push!(_results, result) + !isnothing(is_inverted) && + push!(_attributes, namedattribute("is_inverted", is_inverted)) + !isnothing(is_all) && push!(_attributes, namedattribute("is_all", is_all)) + !isnothing(permutation) && + push!(_attributes, namedattribute("permutation", permutation)) + !isnothing(projected_permutation) && + push!(_attributes, namedattribute("projected_permutation", projected_permutation)) + + return IR.create_operation( + "transform.match.structured.input", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured_num_inits` + +Produces a transform dialect parameter value associated with an integer +attribute containing the number of init(outs) operands of the payload +operation associated with the operand handle. + + +This op can only appear immediately inside a `transform.match.structured` +op and apply to its first block argument because it assumes the payload +to have been already checked for being a single structured op. + + +#### Return modes + +Succeeds if the operand is associated with exactly one structured payload +operation. Produces a silenceable failure otherwise. +""" +function match_structured_num_inits( + operand_handle::Value; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.match.structured.num_inits", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured_num_inputs` + +Produces a transform dialect parameter value associated with an integer +attribute containing the number of input operands of the payload operation +associated with the operand handle. + + +This op can only appear immediately inside a `transform.match.structured` +op and apply to its first block argument because it assumes the payload +to have been already checked for being a single structured op. + + +#### Return modes + +Succeeds if the operand is associated with exactly one structured payload +operation. Produces a silenceable failure otherwise. +""" +function match_structured_num_inputs( + operand_handle::Value; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.match.structured.num_inputs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured` + +Checks if the payload operation associated with the operand handle is a +structured operation, that is, an operation that implements +`LinalgOpInterface`, and that all conditions listed in the body of this +operation are satisfied. Produces a silenceable failure if the payload +operation is not structured. + +The transform operations nested in the body region are applied one by one. +If any of them produces a failure, silenceable or definite, the following +operations are not applied. If the failure propagation mode is \"propagate\", +silenceable failures are forwarded as the result of this operation. If it is +\"suppress\", they are ignored and this operation immediately succeeds. +Definite failures are always propagated immediately. + +In case of success, the transform values produced by this operation are +associated with the same payload as the operands of the block terminator. If +any of the nested operations produced a silenceable failure, regardless of +the failure propagation mode, the transform values produced by this +operation that correspond to the already defined terminator operands are +associated with the same payload as the already defined terminator operands. +Other values produced by this operation are associated with empty payloads. + +If the failure propagation mode is not specified, it is considered +\"propagate\" by default. The \"suppress\" mode can be used to specify optional +matches. + +#### Return modes + +This operation only reads all operand handles and produces all resulting +handles. It succeeds in \"propagate\" mode if the payload operation is a +structured operation and if all the nested operations succeed. It succeeds +in \"suppress\" mode as long as the operand handle is associated with exactly +one payload operation. It produces a definite failure when the handle is +not associated with exactly one payload operation. +""" +function match_structured( + current::Value; + outputs::Vector{IR.Type}, + failure_propagation_mode=nothing, + body_region::Region, + location=Location(), +) + _results = IR.Type[outputs...,] + _operands = Value[current,] + _owned_regions = Region[body_region,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(failure_propagation_mode) && push!( + _attributes, + namedattribute("failure_propagation_mode", failure_propagation_mode), + ) + + return IR.create_operation( + "transform.match.structured", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured_rank` + +Produces a transform dialect parameter value associated with an integer +attribute containing the rank of the structured payload operation associated +with the operand handle. + + +This op can only appear immediately inside a `transform.match.structured` +op and apply to its first block argument because it assumes the payload +to have been already checked for being a single structured op. + + +#### Return modes + +Succeeds if the operand is associated with exactly one structured payload +operation. Produces a silenceable failure otherwise. +""" +function match_structured_rank(operand_handle::Value; rank::IR.Type, location=Location()) + _results = IR.Type[rank,] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.match.structured.rank", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured_result` + +Produces a transform dialect value handle associated with the payload value +defined as a result of the payload operation associated with the operand +handle, or an operation handle to an operation using the produced result +with additional constraints specified by the attributes as follows. + + * If `any` is specified, binds the resulting handle to any operation using + the result and succeeds. + * If `single` is specified, binds the resulting handle to the only + operation using the result or fails if there is more than one (or no) + such operation. + +The number of the result is specified as `position` attribute. It may take +positive and negative values. Negative values are interpreted as counting +results from backwards, e.g., `-1` means the last result and `-2` means the +second-to-last result. In any case, the position must be in bounds for the +given payload operation. A silenceable failure is produced for out-of-bounds +positions. + + +This op can only appear immediately inside a `transform.match.structured` +op and apply to its first block argument because it assumes the payload +to have been already checked for being a single structured op. + + +#### Return modes + +Succeeds if the position is in bounds and if the user operation could be +found when requested. Produces a silenceable failure otherwise. +""" +function match_structured_result( + operand_handle::Value; + result::IR.Type, + position, + any=nothing, + single=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("position", position),] + !isnothing(any) && push!(_attributes, namedattribute("any", any)) + !isnothing(single) && push!(_attributes, namedattribute("single", single)) + + return IR.create_operation( + "transform.match.structured.result", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_structured_yield` + +Forwards the payload association from the operands to the results of the +parent op. Always succeeds. +""" +function match_structured_yield(handles::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[handles...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.match.structured.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`apply_patterns_linalg_erase_unnecessary_inputs` + +Collects patterns that promote inputs to outputs and remove unused inputs of +`linalg.generic` ops. +""" +function apply_patterns_linalg_erase_unnecessary_inputs(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.linalg.erase_unnecessary_inputs", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_linalg_fold_unit_extent_dims_via_reshapes` + +Collects patterns to fold unit-extent dimensions in operands/results of +linalg ops on tensors via reassociative reshape ops. +""" +function apply_patterns_linalg_fold_unit_extent_dims_via_reshapes(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.linalg.fold_unit_extent_dims_via_reshapes", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_linalg_fold_unit_extent_dims_via_slices` + +Collects patterns to fold unit-extent dimensions in operands/results of +linalg ops on tensors via rank-reducing slices. +""" +function apply_patterns_linalg_fold_unit_extent_dims_via_slices(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.linalg.fold_unit_extent_dims_via_slices", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_linalg_tiling_canonicalization` + +Collects canonicalization patterns relevant to apply after tiling patterns. +""" +function apply_patterns_linalg_tiling_canonicalization(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.linalg.tiling_canonicalization", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_bufferize_to_allocation` + +This transform bufferizes the targeted operation and materializes the +result in a new allocation. It replaces all original uses of the target +result with the newly allocated buffer, wrapped in a +`bufferization.to_tensor` op. It returns a handle to the newly allocated +buffer. Furthermore, it returns a handle that is mapped to all newly created +ops. + +Only bufferizable ops are that bufferize to a memory write or have an +aliasing OpOperand (and do not themselves bufferize to an allocation) are +supported. They are bufferized using their BufferizableOpInterface +implementation. E.g.: + +``` +%0 = tensor.insert %f into %dest[%pos] : tensor<10xf32> +``` + +Is bufferized to: + +``` +%alloc = memref.alloc() : memref<10xf32> +bufferization.materialize_in_destination %dest in %alloc +memref.store %f, %alloc[%pos] : memref<10xf32> +%0 = bufferization.to_tensor %alloc restrict writable : memref<10xf32> +``` + +Selected ops that bufferize to an allocation (or need special handling) are +also supported: +- `tensor.pad` is lowered to an allocation, followed by a `linalg.fill` and + and a buffer copy (all on memrefs). +- `vector.mask` is bufferized together with its region. The allocation is + placed in front of the `vector.mask` op. + +An optional memory space attribute can be specified for the materialized +buffer allocation. + +If a memory copy is needed, a \"bufferization.materialize_in_destination\" is +used when possible. This is an op with tensor semantics that will bufferize +to a memory copy later. Which concrete op will be used for the memory copy +is up to the bufferization framework. Alternatively, a custom memcpy op can +be specified via `memcpy_op`. Currently supported are \"memref.copy\" and +\"linalg.copy\". In that case, the source of each memcpy must not have a +custom memory space. Furthermore, because the future buffer layout unknown +for a given tensor, a fully dynamic layout is assumed for best +compatibility. Users should use \"bufferization.materialize_in_destination\" +when possible. + +\"memref.alloc\" is used for new buffer allocations. The buffer is deallocated +at the end of the block if the \"emit_dealloc\" attribute is present. If this +attribute is not present, the allocated memory will be leaked. However, +running the `-buffer-deallocation-pipeline` after all bufferization is done +will properly insert the corresponding deallocation(s). Custom allocation +ops can be specified via `alloc_op`. Currently supported are \"memref.alloc\" +and \"memref.alloca\". In case of a \"memref.alloca\", the buffer is not +deallocated. + +If `bufferize_destination_only` is set, only the destination operands of the +op are bufferized to a new memory allocation, but not the op itself. + +#### Return modes + +This operation consumes the `target` handle and produces the +`allocated_buffer` and `new_ops` handles. It always succeeds. +""" +function structured_bufferize_to_allocation( + target::Value; + allocated_buffer::IR.Type, + new_ops::IR.Type, + memory_space=nothing, + memcpy_op=nothing, + alloc_op=nothing, + bufferize_destination_only=nothing, + emit_dealloc=nothing, + location=Location(), +) + _results = IR.Type[allocated_buffer, new_ops] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(memory_space) && + push!(_attributes, namedattribute("memory_space", memory_space)) + !isnothing(memcpy_op) && push!(_attributes, namedattribute("memcpy_op", memcpy_op)) + !isnothing(alloc_op) && push!(_attributes, namedattribute("alloc_op", alloc_op)) + !isnothing(bufferize_destination_only) && push!( + _attributes, + namedattribute("bufferize_destination_only", bufferize_destination_only), + ) + !isnothing(emit_dealloc) && + push!(_attributes, namedattribute("emit_dealloc", emit_dealloc)) + + return IR.create_operation( + "transform.structured.bufferize_to_allocation", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_continuous_tile_sizes` + +This transform emits the IR computing the list of (1) exponentially +diminishing tile sizes that are powers of 2; and (2) the corresponding +chunk-sizes the target op should be split into along the given dimension. + +For example, for `target_size` 9, and `dimension` 0 for the following +linalg op as target + +``` + %0 = linalg.matmul ins(%arg0, %arg1: tensor<25x34xf32>, tensor<34x25xf32>) + outs(%arg2: tensor<25x25xf32>) +``` + +the first result `tile_sizes` will be a list of diminishing tile sizes +9, 4, 2, 1; and the second result will be a list of chunk sizes +18, 4, 2, 1 that the corresponding dimension should be split into. + +After the target op has been split along the given dimension (for example +using multiway split), each chunk can be tiled with the corresponding tile +size in the `tile_sizes` list generated as a result of this op. + +Specifying the output type as !transform.param will cause `tile_sizes` +and `chunk_sizes` to be computed statically and not dynamically. +""" +function structured_continuous_tile_sizes( + target::Value; + tile_sizes::IR.Type, + chunk_sizes::IR.Type, + dimension, + target_size, + location=Location(), +) + _results = IR.Type[tile_sizes, chunk_sizes] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("dimension", dimension), namedattribute("target_size", target_size) + ] + + return IR.create_operation( + "transform.structured.continuous_tile_sizes", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_convert_conv2d_to_img2col` + +Convert linalg.conv_2d_xxx into linalg.generic (for img2col packing) +and linalg.matmul. + +A convolution operation can be written as a matrix-matrix multiplication by +unfolding the cross-correlation between input and filter and explicitly copy +overlapped sliding window inputs. + +Consider 2D input X with single channel input and output and 2x2 filter W: +``` +[x(0, 0) , x(0, 1) , ..., x(0, n) ] +[x(1, 0) , x(1, 1) , ..., x(1, n) ] +[. , . ,. , . ] [w(0, 0), w(0, 1)] +[. , . , . , . ] (conv) [w(1, 0), w(1, 1)] +[. , . , ., . ] +[x(n-1, 0), x(n-1, 1), ..., x(n-1, n-1)] +``` + +The packed input data (img2col) is a matrix with |rows| = output spatial +size, |columns| = filter spatial size. To compute the output Y(i, j) we need +to calculate the dot product between filter window at input X(x, y)) and the +filter which will look like the following where r.h.s is the img2col matrix +and l.h.s is the flattned filter: +``` +[x(0,0), x(0,1), x(1,0), x(1,1)] +[x(0,1), x(1,1), x(0,2), x(1,2)] (matmul) [w(0,0), w(0,1), w(1,0), w(1,1)] +[x(0,1), x(1,1), x(0,2), x(1,2)] +[ . , . , . , . ] +``` + +In general for 2D case with (N, H, W, C) input and (Kh, Kw, C, D) filter +and output (N, Ho, Wo, D) the convolution is the following matrix-matrix +multiplication (Ho x Wo, Kh x Kw x C) * (Kh x Kw x C, D) for each input in +the N input. For the case where N > 1 its a batched matrxi-matrix +multplication. + +Returns two handles: +- One on the operation that produces the img2col tensor. +- One on the final operation of the sequence that replaces the original + convolution. + +#### Return modes: + +Returns a definite failure if target is not isolated from above. +Returns a silenceable failure if the pattern application failed. +""" +function structured_convert_conv2d_to_img2col( + target::Value; img2col_tensor::IR.Type, transformed::IR.Type, location=Location() +) + _results = IR.Type[img2col_tensor, transformed] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.convert_conv2d_to_img2col", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_convert_to_loops` + +For operations that implement the `TilingInterface`, and implement +the `generateScalarImplementation` method, lowers the operation to +loops. The return handle points to all generated loops. +Fails if the payload ops cannot be lowered to loops. +""" +function structured_convert_to_loops(target::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.convert_to_loops", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_decompose_interface` + +TODO +""" +function structured_decompose_interface( + target::Value; transformed::IR.Type, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.decompose_interface", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_decompose` + +Decomposes named complex operations, such as higher-dimensional +(depthwise) convolutions, into combinations of lower-dimensional equivalents +when possible. + +#### Return modes + +This operation ignores non-Linalg ops and drops them in the return. +If all the operations referred to by the `target` handle decompose +properly, the transform succeeds. Otherwise the transform produces a +silenceable failure. The return handle points to only the subset of +successfully produced computational operations, which can be empty. +""" +function structured_decompose(target::Value; transformed::IR.Type, location=Location()) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.decompose", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_eliminate_empty_tensors` + +Try to eliminate all `tensor.empty` op uses that are anchored on a LinalgOp +within the targeted op. + +This op is similar to `bufferization.eliminate_empty_tensors`, but specific +to LinalgOps. + +`tensor.empty` ops cannot be bufferized. They can either be converted to +`bufferization.alloc_tensor` or replaced with another tensor (via this +transform). `tensor.empty` does not specify the contents of the returned +tensor so their results can be replaced with arbitrary tensor values as long +as the dimensions match. + +This transform looks for `tensor.empty` ops where the SSA use-def chain of +the result ends in a supported LinalgOp (always following the aliasing +OpOperand/OpResult chain). The following LinalgOps are supported: +- Only parallel iterator types. +- The use-def chain ends in an input operand of the LinalgOp. +- The LinalgOp has an unused output operand with the same shape and + indexing map. + +# Example + +``` +%0 = tensor.empty() +%1 = linalg.matmul ins(...) outs(%0) +%2 = linalg.generic ins(%1) outs(%dest) { + ^bb0(%in: f32, %out: f32): + // out not used +} +``` + +Is rewritten with: +``` +%0 = tensor.empty() +%1 = linalg.matmul ins(...) outs(%dest) +%2 = linalg.generic ins(%0) outs(%1) { + ^bb0(%in: f32, %out: f32): + // Use %out instead of %in +} +``` + +After this transformation, the \"ins\" operand has no uses inside the body of +the LinalgOp and can be folded away with existing cleanup patterns. +Afterwards, the tensor::EmptyOp can also fold away, so that the example can +bufferize without an allocation (in the absence of other conflicts). + +#### Return modes + +This transform reads the target handle and modifies the payload. It does +not produce any handle. +""" +function structured_eliminate_empty_tensors(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.eliminate_empty_tensors", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_flatten_elementwise` + +Flattens the iteration space and (applicable) operands of elementwise +linalg ops to a single dimension. + +Returns one handle: +- Flattened linalg operation. + +#### Return modes: + +Returns a definite failure if target is not isolated from above. +Returns a silenceable failure if the pattern application failed. +""" +function structured_flatten_elementwise( + target::Value; transformed::IR.Type, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.flatten_elementwise", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_fuse_into_containing_op` + +Fuses the `producer_op` into the `containing_op`. +Returns a handle to the fused ops and the `new_containing_op`. + +The producer is typically a slice of a tileable op (i.e., implements +TilingInterface). In that case, this transform computes the accessed +producer slice inside of the containing op (\"tile and fuse\") and if required, +creates a new containing op with outputs from the fused producer. Otherwise, +the entire producer is cloned inside the containing op (\"clone and fuse\"). + +The containing op handle must be associated with exactly one payload op. The +producer op handle may be associated with multiple payload ops. This +transform fuses producers one-by-one, always picking an unspecified producer +that has at least one use inside the containing op among the +producers. A producer can be listed multiple times in the handle. + +Note: If a producer has multiple uses inside the containing op, it is +currently tiled and/or cloned multiple times into the containing op. +TODO: Reuse already fused OpResults instead of tiling/cloning a second time +when possible. Fuse producers according to a topological sorting to achieve +the largest amount of reuse. + +#### Return modes + +If at least one producer could not be fused, this operation produces a +silenceable failure. This is the case when tiling fails or when no +producer op could be found among the remaining producers that has at least +one use within the containing op. I.e., \"producers\" that are not consumed +within the containing op are rejected by this operation. + +This operation consumes the producer handle. +This operation only reads the containing op handle. +""" +function structured_fuse_into_containing_op( + producer_op::Value, + containing_op::Value; + fused_op::IR.Type, + new_containing_op::IR.Type, + location=Location(), +) + _results = IR.Type[fused_op, new_containing_op] + _operands = Value[producer_op, containing_op] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.fuse_into_containing_op", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_fuse` + +Tiles the operations pointed to by the target handle and fuses their +producers greedily using the options provided as attributes. +""" +function structured_fuse( + target::Value; + transformed::IR.Type, + loops::Vector{IR.Type}, + tile_sizes=nothing, + tile_interchange=nothing, + location=Location(), +) + _results = IR.Type[transformed, loops...] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(tile_sizes) && push!(_attributes, namedattribute("tile_sizes", tile_sizes)) + !isnothing(tile_interchange) && + push!(_attributes, namedattribute("tile_interchange", tile_interchange)) + + return IR.create_operation( + "transform.structured.fuse", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_generalize` + +Transforms a named structured operation into the generic form with the +explicit attached region. + +#### Return modes + +This operation ignores non-Linalg ops and drops them in the return. +If all the operations referred to by the `target` handle generalize +properly, the transform succeeds. Otherwise the transform produces a +silenceable failure. The return handle points to only the subset of +successfully produced equivalent generic operations, which can be empty or +contain the original ops if they were already in generic form. +""" +function structured_generalize(target::Value; transformed::IR.Type, location=Location()) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.generalize", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_hoist_pad_build_packing_loop_nest` + +Helper transform used to hoist a tensor.pad target operation. This operation +creates the packing loop nest required by the hoist_pad operation and makes +that functionality available independently. + +TODO: In the future, we should consider rewriting as a tensor.pack after +hoisting since this abstraction is now available. + +#### Return modes + +This operation ignores non-tensor.pad ops and drops them in the result. +If any non-tensor.pad is passed, the transform emits a silenceable failure. + +The return handle points to only the subset of successfully created packing +loop nests, which can be empty. +""" +function structured_hoist_pad_build_packing_loop_nest( + target::Value, + loop::Value; + packing_loop::IR.Type, + transpose=nothing, + location=Location(), +) + _results = IR.Type[packing_loop,] + _operands = Value[target, loop] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(transpose) && push!(_attributes, namedattribute("transpose", transpose)) + + return IR.create_operation( + "transform.structured.hoist_pad.build_packing_loop_nest", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_hoist_pad` + +Hoist the tensor.pad target operation by at most the given number of loops. +Optionally apply the transpose attribute to the inner dimensions. + +TODO: In the future, we should consider rewriting as a tensor.pack after +hoisting since this abstraction is now available. +TODO: Maybe also return the linalg.generic transpose created at some point. + +#### Return modes + +This operation ignores non-tensor.pad ops and drops them in the result. +If any non-tensor.pad is passed, the transform emits a silenceable failure. + +If all the operations referred to by the `target` handle padproperly, the +transform succeeds. Otherwise the transform produces a silenceable failure. + +The return handle points to only the subset of successfully hoisted +tensor.pad operations, which can be empty. +""" +function structured_hoist_pad( + target::Value; transformed::IR.Type, num_loops, transpose=nothing, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("num_loops", num_loops),] + !isnothing(transpose) && push!(_attributes, namedattribute("transpose", transpose)) + + return IR.create_operation( + "transform.structured.hoist_pad", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_hoist_redundant_vector_broadcasts` + +Hoist vector.extract / vector.broadcasts pairs out of immediately +enclosing scf::ForOp iteratively. + +#### Return modes: + +The operation always succeeds and returns a handle to the transformed +function op. +""" +function structured_hoist_redundant_vector_broadcasts( + target::Value; transformed::IR.Type, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.hoist_redundant_vector_broadcasts", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_hoist_redundant_vector_transfers` + +Hoist vector.transfer_read / vector.transfer_write pairs out of immediately +enclosing scf::ForOp iteratively, if the following conditions are true: + 1. The 2 ops access the same memref with the same indices. + 2. All operands are invariant under the enclosing scf::ForOp. + 3. No uses of the memref either dominate the transfer_read or are + dominated by the transfer_write (i.e. no aliasing between the write and + the read across the loop) + +WARNING: This hoisting does not model parallelism and is generally incorrect +when used on distributed loops with memref semantics! +TODO: obsolete and should be retired. + +#### Return modes: + +The operation always succeeds and returns a handle to the transformed +function op. +""" +function structured_hoist_redundant_vector_transfers( + target::Value; transformed::IR.Type, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.hoist_redundant_vector_transfers", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_insert_slice_to_copy` + +Targeted rewrite of an tensor.insert_slice to linalg.copy. +This is useful to materialize copies explicitly before bufferization and +transform them, avoiding the need to rediscover them after bufferization. + +If the insert_slice source is already a linalg.copy, only return the source +op (i.e. do not create an additional linalg.copy op). + +#### Return modes: + +The operation always succeeds and returns a handle to the relevant +linalg.copy op. +""" +function structured_insert_slice_to_copy( + target::Value; transformed::IR.Type, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.insert_slice_to_copy", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_interchange` + +Interchanges the iterators of the operations pointed to by the target handle +using the iterator interchange attribute. + +#### Return modes + +This operation ignores non-linalg::Generic ops and drops them in the return. +This operation fails if the interchange attribute is invalid. +If all the operations referred to by the `target` handle interchange +properly, the transform succeeds. +If any interchange fails, the transform produces a definite failure. +The return handle points to only the subset of successfully produced +interchanged operations, which can be empty. +""" +function structured_interchange( + target::Value; transformed::IR.Type, iterator_interchange=nothing, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(iterator_interchange) && + push!(_attributes, namedattribute("iterator_interchange", iterator_interchange)) + + return IR.create_operation( + "transform.structured.interchange", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_lower_pack` + +Rewrite a tensor.pack into tensor.pad + tensor.expand_shape + linalg.transpose. + +#### Return modes + +This operation ignores non-pack ops and drops them in the return. +This operation produces a silenceable failure if the rewrite fails for any +reason. +If all the operations referred to by the `target` are rewritten, the +transform succeeds. +Return handles to the newly produced pad, expand_shape and transpose ops. +""" +function structured_lower_pack( + target::Value; + pad_op::IR.Type, + expand_shape_op::IR.Type, + transpose_op::IR.Type, + location=Location(), +) + _results = IR.Type[pad_op, expand_shape_op, transpose_op] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.lower_pack", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_lower_unpack` + +Lower a tensor.unpack into empty + linalg.transpose + tensor.collapse_shape + +tensor.extract_slice. + +#### Return modes + +This operation ignores non-unpack ops and drops them in the return. +This operation produces a silenceable failure if the rewrite fails for any +reason. +If all the operations referred to by the `target` are rewritten, the +transform succeeds. +Return handles to the newly produced empty, transpose, collapse_shape and extract_slice ops. +""" +function structured_lower_unpack( + target::Value; + empty_op::IR.Type, + transpose_op::IR.Type, + collapse_shape_op::IR.Type, + extract_slice_op::IR.Type, + location=Location(), +) + _results = IR.Type[empty_op, transpose_op, collapse_shape_op, extract_slice_op] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.lower_unpack", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_gpu_map_copy_to_threads` + +Targeted mapping of a linalg.copy / tensor.pad operation on tensors to a GPU +thread mapping. + +This operation implements a greedy heuristic that determines a good +distribution of threads to break down the copy/pad operation into. +The heuristic is driven by considerations related to the underlying +architecture for which good high-level decisions are needed assuming certain +hardware features. Relevant features are exposed via first-class attributes +to control the behavior of the transformation at a high level. + +For now, a single heuristic is implemented and can be extended on a per-need +basis. + +#### Return modes + +This operation fails definitely if there is an unsupported op (i.e., not +linalg.copy / tensor.pad) among the targeted op. Otherwise, the operation +always succeeds and returns a handle to the relevant tiled linalg.copy / +tensor.pad op and the enclosing scf.forall op. +""" +function structured_gpu_map_copy_to_threads( + target::Value; + forall_op::IR.Type, + tiled_op::IR.Type, + total_num_threads, + desired_bit_alignment, + location=Location(), +) + _results = IR.Type[forall_op, tiled_op] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("total_num_threads", total_num_threads), + namedattribute("desired_bit_alignment", desired_bit_alignment), + ] + + return IR.create_operation( + "transform.structured.gpu.map_copy_to_threads", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_match` + +Match op with the specified constraints, within the target op. + +The following constraints are supported: + - interface: an optional MatchInterfaceEnum specifying an enum + representation for an interface to target. + - ops: an optional StrArrayAttr specifying the concrete name of an op. + Multiple names can be specified. Matched ops must have one of specified + names. + - attribute: the matched op must have all specified attributes (with their + specified values). + - filter_result_type: the matched op must return exactly this one type. + - filter_operand_types: all the operands of the matched op must must be of + this type. If more than a type is specified, then the length of the list + must be equal to the number of operands in the matched op, and the match + will succeed only if the operand types match all the types in the list + in the order in which they are specified. + +Note: Only ops that satisfy all specified constraints are matched. + +TODO: Extend with regions to allow a limited form of constraints. + +#### Return modes + +This op traverses the ops nested under `target` and returns the handles to +all the operations that match the requirements. + +This op fails if the target is not a handle to exactly one operation. +Otherwise it succeeds. + +This operation does not consume the target handle and produces new handles: +it is a navigation op. +""" +function structured_match( + target::Value; + results::IR.Type, + ops=nothing, + interface=nothing, + op_attrs=nothing, + filter_result_type=nothing, + filter_operand_types=nothing, + location=Location(), +) + _results = IR.Type[results,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(ops) && push!(_attributes, namedattribute("ops", ops)) + !isnothing(interface) && push!(_attributes, namedattribute("interface", interface)) + !isnothing(op_attrs) && push!(_attributes, namedattribute("op_attrs", op_attrs)) + !isnothing(filter_result_type) && + push!(_attributes, namedattribute("filter_result_type", filter_result_type)) + !isnothing(filter_operand_types) && + push!(_attributes, namedattribute("filter_operand_types", filter_operand_types)) + + return IR.create_operation( + "transform.structured.match", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_multitile_sizes` + +Emits the IR computing the tile sizes `s1` and `s2` such that: + + - there exists a combination of `n` tiles of size `s1` and `m` tiles of + size `s2` that covers the entirety of the iteration space `dimension` of + the target structured op; + - `s1`, `s2` is less than or equal to `target_size`; + - `s1` and `s2` are divisible by `divisor. + +For example, for a dimension of size 54 with target size 12 and divisor 2, +this can emit the IR computing the tile size 10, used for 3 tiles, and 12, +used for 2 tiles, totally 10*3 + 12*2 = 54. Note that when the divisor does +not divide the original dimension size, it is impossible to compute such +tile sizes. An assertion is emitted to guard against this in the dynamic +case. + +Expects the target size and the divisor to be strictly positive. Folds the +IR as much as possible, normally obtaining constant sizes and numbers of +tiles for a statically known dimension. + +This does *not* consume the target handle and produces three handles each +pointing to single-result index-typed operations (which may be arithmetic +constant operations) defining the two respective tile sizes and the product +of the first tile size with the number of tiles of that size (useful for +splitting the iteration space). + +This operation composes with the regular tiling when applied per-dimension: + +```mlir +%sz1, %sz2, %split = structured.multitile_sizes %target + { target_size = 10, dimension = 1 } + : !transform.any_op, !transform.param, + !transform.param, !transform.param +%low, %high = structured.split %target after %split { dimension = 1 } + : !transform.any_op, !transform.param +%tiled_low, %loop1 = structured.tile_using_for %low [0, %sz1] + : (!transform.any_op, !transform.param) + -> (!transform.any_op, !transform.any_op) +%tiled_high, %loop2 = structured.tile_using_for %high [0, %sz2] + : (!transform.any_op, !transform.param) + -> (!transform.any_op, !transform.any_op) +%common = merge_handles %tiled_low, %tiled_high : !transform.any_op + +%sz3, %sz4, %split = structured.multitile_size %target + { target_size = 42, dimension = 0 } + : !transform.any_op, !transform.any_op, + !transform.any_op, !transform.any_op +%sz3r, %sz4r, %splitr = replicate num(%common) %sz3, %sz4, %splitr + : !transform.any_op, !transform.any_op, !transform.any_op +structured.split %common after %splitr { dimension = 0 } + : !transform.any_op, !transform.any_op +// ... +``` +""" +function structured_multitile_sizes( + target::Value; + low_size::IR.Type, + high_size::IR.Type, + split_point::IR.Type, + dimension, + target_size, + divisor=nothing, + location=Location(), +) + _results = IR.Type[low_size, high_size, split_point] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("dimension", dimension), namedattribute("target_size", target_size) + ] + !isnothing(divisor) && push!(_attributes, namedattribute("divisor", divisor)) + + return IR.create_operation( + "transform.structured.multitile_sizes", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_pack_greedily` + +Target a Linalg op and rewrite it into packed LinalgOp form by trying to +infer whether a known suboperation is embedded + +Different packing strategies are applied in order, when one applies +successfully, the transform returns: + 1. Matmul packing: Try to infer a matmul operation embedded in the target op. + Specifically, this looks for 2 parallel dimensions that participate in + an outer-product and 1 reduction dimension. + These dimensions are referred as (m, n, k) to match canonical matmul + terminology. + + The packed sizes for (m, n, k) are specified by `matmul_packed_sizes` + and the optional `matmul_padded_sizes_next_multiple_of`. + When an entry `matmul_packed_sizes[i]` is non-0, the corresponding + dimension is packed by `matmul_packed_sizes[i]`. + Otherwise, the dimension is merely padded to the next multiple of + `matmul_padded_sizes_next_multiple_of[i]`. + + `matmul_padded_sizes_next_multiple_of` is optional and is expected to + either be empty or of size `3`, matching the size of `matmul_packed_sizes`. + For each individual element of `matmul_packed_sizes` and + `matmul_padded_sizes_next_multiple_of`, only one of them is allowed to + be non-zero. + + The ordering of the packed dimensions (mm, nn, kk) is specified by the + `matmul_inner_dims_order` attribute. + +Packing occurs as follows: + 1. Find the dimensions to pack according to the strategy. + 2. The target is converted to linalg.generic form. + 3. An interchange transform is applied to isolate the dimensions to pack as + the most minor indexing dimensions of the linalg.generic. The most minor + dimensions are themselves ordered according to `inner_dims_order`. + 4. An elementwise traversal of `matmul_packed_sizes` and + `matmul_padded_sizes_next_multiple_of` is performed and for each + dimension `d`, either pack to `matmul_packed_sizes[d]` or pad to the + `matmul_padded_sizes_next_multiple_of[d]`. + 5. Packing/padding is performed by the amounts determined in step 4. and + following `inner_dims_order`. + +By normalizing the most minor dimensions to `inner_dims_order`, the transform +guarantees that packing immediately generates inner dimensions in a desirable +layout. + +Outer dimension layout permutations are not controlled by this transform op +at the moment and can be obtained by composing with the pack_transpose +transformation. + +#### Return modes + +This operation ignores non-Linalg ops and drops them in the return. +It returns the list of packed Linalg ops or the original op when all available +packing strategies failed to apply. +""" +function structured_pack_greedily( + target::Value, + matmul_packed_sizes::Vector{Value}; + packed_op::IR.Type, + static_matmul_packed_sizes=nothing, + matmul_padded_sizes_next_multiple_of=nothing, + matmul_inner_dims_order=nothing, + location=Location(), +) + _results = IR.Type[packed_op,] + _operands = Value[target, matmul_packed_sizes...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(static_matmul_packed_sizes) && push!( + _attributes, + namedattribute("static_matmul_packed_sizes", static_matmul_packed_sizes), + ) + !isnothing(matmul_padded_sizes_next_multiple_of) && push!( + _attributes, + namedattribute( + "matmul_padded_sizes_next_multiple_of", matmul_padded_sizes_next_multiple_of + ), + ) + !isnothing(matmul_inner_dims_order) && push!( + _attributes, namedattribute("matmul_inner_dims_order", matmul_inner_dims_order) + ) + + return IR.create_operation( + "transform.structured.pack_greedily", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_pack` + +Pack a LinalgOp by applying a data tiling transformation on the op and +packing the operands according to the `packed_sizes` specification. + +Iterator dimensions are tiled in their canonical order in the op spec. +Operands are packed according to the same canonical order of the op iterator +dimensions. + +Specifying a packed size of 0 for an iterator removes it from consideration +for packing. + +`tensor.pack` (resp. `tensor.unpack`) operations are inserted for the operands +(resp. results) that need to be packed (resp. unpacked) according to the +`packed_sizes` specification. + +#### Example + +Consider a `linalg.matmul` with indexing maps: +``` + // M N K M K + // affine_map<(d0, d1, d2) -> (d0, d2)> + // K N + // affine_map<(d0, d1, d2) -> (d2, d1)> + // M N + // affine_map<(d0, d1, d2) -> (d0, d1)> + %0 = linalg.matmul ins(%A, %B: tensor, tensor) + outs( %C: tensor) +``` + +Specifying packed_sizes [2, 3, 4] results in tiling the iterator dimensions +M, N and K, in this order, in both the op and its operands. +``` + // M N K m n k M K m k + // affine_map<(d0, d1, d2, d3, d4, d5) -> (d0, d2, d3, d5)> + // K N n k + // affine_map<(d0, d1, d2, d3, d4, d5) -> (d2, d1, d4, d5)> + // M N m n + // affine_map<(d0, d1, d2, d3, d4, d5) -> (d0, d1, d3, d4)> + %0 = linalg.generic_representing_some_higher_d_matmul + ins(%A, %B: tensor, tensor) + outs( %C: tensor) +``` +In particular, note that the second operand `B` has shape `KxNxnxk` (and not +`KxNxkxn` as one could expect by looking **only** at the operand). + +Other layouts can be obtained unsurprisingly from this canonical +transformation by composing the resulting operation with a +`transform.structured.pack_transpose` op. +This composition allows separating concerns and composes better compared +to adding additional permutation attributes to this transform op. + +#### Return modes + +This operation applies to a single Linalg op, otherwise it fails. +This operation may produce a definite failure if the packing fails for any +reason. + +The returned handle point to the packed LinalgOp. +""" +function structured_pack( + target::Value, + packed_sizes::Vector{Value}; + packed_op::IR.Type, + static_packed_sizes=nothing, + location=Location(), +) + _results = IR.Type[packed_op,] + _operands = Value[target, packed_sizes...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(static_packed_sizes) && + push!(_attributes, namedattribute("static_packed_sizes", static_packed_sizes)) + + return IR.create_operation( + "transform.structured.pack", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_pack_transpose` + +Apply a transposition to a single `tensor.pack` (resp. `tensor.unpack`) and +update the `linalg.generic` op that consumes (resp. produces) the operation. + +This transform allows composing a simple `structured.pack` with additional +transpositions to e.g. match the data format required by a specific library +call or ISA instruction. + +The transpose spec must specify at least one of `outer_perm` or `inner_perm` +attributes, which will act upon the `outer_dims_perm` or `inner_dims_pos` of +the specified `tensor.pack` or `tensor.unpack` op. + +If the `target` of this op is a `tensor.pack` then a new `tensor.empty` will +be created along with transposed versions of the `tensor.pack` and the +consuming `linalg.generic`, which is expected to be the sole consumer. + +If the `target` of this op is a `tensor.unpack` then the whole pack / compute +/ unpack chain will be transposed and transposed clones of `tensor.pack`, +the consuming `linalg.generic` and the tail `tensor.pack` will be created. + +#### Return modes + +This operation targets a single `tensor.pack` / `tensor.unpack` op and a +single matching `linalg.generic` that consumes / produces the op. Otherwise, +it produces a silenceableFailure. + +This operation may produce a silenceableFailure if the transpose spec is +ill-formed (i.e. `outer_perm` or `inner_perm` are not permutations of the +proper rank) or if the tranposition of all involved operations fails for any +reason. + +This operation returns 3 handles, one to the transformed LinalgOp, one to +the transformed `tensor.pack` and one to the transformed `tensor.unpack`. +The last handle for `tensor.unpack` is empty if `target_pack_or_unpack_op` +was not itself a `tensor.unpack`. +""" +function structured_pack_transpose( + target_pack_or_un_pack_op::Value, + target_linalg_op::Value; + packed_op::IR.Type, + pack_op::IR.Type, + un_pack_op::IR.Type, + outer_perm=nothing, + inner_perm=nothing, + location=Location(), +) + _results = IR.Type[packed_op, pack_op, un_pack_op] + _operands = Value[target_pack_or_un_pack_op, target_linalg_op] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(outer_perm) && push!(_attributes, namedattribute("outer_perm", outer_perm)) + !isnothing(inner_perm) && push!(_attributes, namedattribute("inner_perm", inner_perm)) + + return IR.create_operation( + "transform.structured.pack_transpose", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_pad` + +Pads the operations pointed to by the target handle using the options +provides as operation attributes. The operation returns a handle to the +padded operation and to the padding operation (\"tensor.pad\"). + +To preserve tensor SSA use-def chains, the unpadded result is copied back to +the original destination tensor of the targeted op. The op that copies back +the result can be customized with `copy_back_op`: + +* \"bufferization.materialize_in_destination\" (default) +* \"linalg.copy\" +* \"none\" (no copy back) + +#### Return modes + +This operation ignores non-Linalg ops and drops them in the return. +This operation may produce a definite failure if the padding fails for any +reason. + +If all the operations referred to by the `target` handle pad +properly, the transform succeeds. Otherwise the transform produces a +silenceable failure. +The return handle points to only the subset of successfully produced +padded operations, which can be empty. +""" +function structured_pad( + target::Value, + pad_to_multiple_of::Vector{Value}; + padded::IR.Type, + pad::IR.Type, + copy::IR.Type, + padding_values=nothing, + padding_dimensions=nothing, + static_pad_to_multiple_of=nothing, + pack_paddings=nothing, + transpose_paddings=nothing, + copy_back_op=nothing, + location=Location(), +) + _results = IR.Type[padded, pad, copy] + _operands = Value[target, pad_to_multiple_of...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(padding_values) && + push!(_attributes, namedattribute("padding_values", padding_values)) + !isnothing(padding_dimensions) && + push!(_attributes, namedattribute("padding_dimensions", padding_dimensions)) + !isnothing(static_pad_to_multiple_of) && push!( + _attributes, + namedattribute("static_pad_to_multiple_of", static_pad_to_multiple_of), + ) + !isnothing(pack_paddings) && + push!(_attributes, namedattribute("pack_paddings", pack_paddings)) + !isnothing(transpose_paddings) && + push!(_attributes, namedattribute("transpose_paddings", transpose_paddings)) + !isnothing(copy_back_op) && + push!(_attributes, namedattribute("copy_back_op", copy_back_op)) + + return IR.create_operation( + "transform.structured.pad", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_promote` + +Promotes the specified operands of the target into a separate memory buffer. + +At this point, this transform does not allow customizing alloc/dealloc +functions nor the behavior on copy in/out operations. + +#### Return modes + +This operation applies to a single Linalg op that satisfies the +`promoteSubviewsPrecondition`, otherwise it fails. + +If the operations referred to by the `target` handle promote +properly, the transform succeeds. + +When successful, the return handle points to the \$target operation that +was modified inplace. +""" +function structured_promote( + target::Value; + transformed::IR.Type, + operands_to_promote=nothing, + use_full_tile_buffers=nothing, + use_full_tiles_by_default=nothing, + use_alloca=nothing, + memory_space=nothing, + mapping=nothing, + alignment=nothing, + location=Location(), +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(operands_to_promote) && + push!(_attributes, namedattribute("operands_to_promote", operands_to_promote)) + !isnothing(use_full_tile_buffers) && + push!(_attributes, namedattribute("use_full_tile_buffers", use_full_tile_buffers)) + !isnothing(use_full_tiles_by_default) && push!( + _attributes, + namedattribute("use_full_tiles_by_default", use_full_tiles_by_default), + ) + !isnothing(use_alloca) && push!(_attributes, namedattribute("use_alloca", use_alloca)) + !isnothing(memory_space) && + push!(_attributes, namedattribute("memory_space", memory_space)) + !isnothing(mapping) && push!(_attributes, namedattribute("mapping", mapping)) + !isnothing(alignment) && push!(_attributes, namedattribute("alignment", alignment)) + + return IR.create_operation( + "transform.structured.promote", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_replace` + +Replace all `target` payload ops with the single op that is contained in +this op\'s region. All targets must have zero arguments and must be isolated +from above. + +This op is for debugging/experiments only. + +#### Return modes + +This operation consumes the `target` handle. +""" +function structured_replace( + target::Value; replacement::IR.Type, bodyRegion::Region, location=Location() +) + _results = IR.Type[replacement,] + _operands = Value[target,] + _owned_regions = Region[bodyRegion,] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.replace", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_rewrite_in_destination_passing_style` + +Rewrite a supported tensor operation that is not in destination-passing style +into a form that is in destination-passing style. +Currently supported operations are: + - tensor.pad + - tensor.generate + - tensor.from_elements +This dichotomy hints at a future interface, for now the implementation just +switches between different implementation. + +#### Return modes + +This operation ignores non-unsupported ops and drops them from the return. +If all the operations referred to by the `target` handle generalize +properly, the transform succeeds. Otherwise the transform produces a +silenceable failure. +The return handle points to a subset of successfully produced operations: + - `tensor.pad` case, the returned handle points to the tensor.insert_slice. + - `tensor.generate` case, the returned handle points to the linalg.generic. + - `tensor.from_elements` case, the returned handle points to the last + `tensor.insert`. +""" +function structured_rewrite_in_destination_passing_style( + target::Value; transformed::IR.Type, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.rewrite_in_destination_passing_style", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_scalarize` + +Indicates that ops of a specific kind in the given function should be +scalarized (i.e. their dynamic dimensions tiled by 1). + +#### Return modes: + +This operation ignores non-Linalg ops and drops them in the return. +This operation produces definite failure if the scalarization fails for any +reason. +If all the operations referred to by the `target` handle scalarize +properly, the transform succeeds. Otherwise the transform produces a +silenceable failure. + +The return handle points to only the subset of successfully produced +tiled-by-1 operations, which can be empty. + +This operation does not return handles to the tiled loop. +We make this design choice because it is hard to know ahead of time the +number of loops that will be produced (it depends on the number of dynamic +dimensions after multiple transformations have been applied). +Loops can always be recovered by navigating from the tiled operations if +needed. +""" +function structured_scalarize(target::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.scalarize", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_specialize` + +Transforms a generic operation into the equivalent named form. + +#### Return modes + +This operation ignores non-Linalg ops and drops them in the return. If all +the operations referred to by the `target` handle specialize, the transform +succeeds; otherwise, the operation produces a silenceable failure. The return +handle points to only the subset of successfully produced equivalent named +operations, which can be empty or contain the original ops if they were already +in named form. The supported specialization to named Linalg operations are: +- linalg.copy of any rank. +""" +function structured_specialize(target::Value; transformed::IR.Type, location=Location()) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.specialize", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_split` + +Splits the given `target` op into two or more complementary +parts, which combined cover the entire iteration domain of the original op. +The split is performed along the iteration space dimension provided as +chunk size attribute specifying the size of the lower part; the remaining +range in the iteration space is assigned as the upper part. In case of +dimension overflow, the transformation fails. The split is performed at the +dimension iterator value specified as either the static chunk size +attribute when it is known at transform IR construction time or +as the handle to an operation producing a single index-typed value +when it is computed by payload IR. In the latter case, the chunk size +point must be set to `ShapedType::kDynamic` and the dynamic size handle +must point to as many value-producing operations as there are structured +operations pointed to by the target handle. + +The operation consumes the target handle, but preserves the chunk size +handle if provided. Without the `multiway` attribute, it produces two +new handles pointing to the two parts of the structured op after splitting, +in the same order as the target operand, with the first handle +corresponding to the part with lower iteration space indices. + +Multiway split mode is enabled by specifying the `multiway` attribute. +In this mode a single `target` op is split into multiple parts covering +the iteration space of the specified dimension. `static_chunk_sizes` and +`dynamic_chunk_sizes` in this case is a list of chunk sizes that the given +dimension should be split into. With `multiway` it produces two handles; +the first handle is a list of the multiple parts of the structured op +after splitting, where the target dimensions for each linalg op in the +list corresponds to the chunk sizes specfied in the input split list. +If the chunk sizes do not cover the entire iteration space, the leftover +chunk is the last payload in the first handle. The second handle is empty. +""" +function structured_split( + target::Value, + dynamic_chunk_sizes=nothing::Union{Nothing,Value}; + first::IR.Type, + second::IR.Type, + dimension, + static_chunk_sizes, + multiway=nothing, + location=Location(), +) + _results = IR.Type[first, second] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("dimension", dimension), + namedattribute("static_chunk_sizes", static_chunk_sizes), + ] + !isnothing(dynamic_chunk_sizes) && push!(_operands, dynamic_chunk_sizes) + !isnothing(multiway) && push!(_attributes, namedattribute("multiway", multiway)) + + return IR.create_operation( + "transform.structured.split", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_split_reduction` + +Indicates that the given `target` op should be transformed with the +`splitReduction` transformation and split factor provided as attribute. + +The `splitReduction` transformation splits the first single linalg op +reduction into a parallel and reduction dimension. +A new `linalg.generic` op is created to perform the rest of the reduction. + +The transformation supports different configurations attributes: + - split_factor: the factor by which to split (i.e. the size of the + remaining reduction after splitting). + - insert_split_dimension: the dimension in the temporary tensor into + which the new parallel dimension is inserted. + - inner_parallel: specifies whether the parallel dimension is before or + after the reduction dimension in the splitting op. + - use_scaling_algorithm: whether to use a scaling based formulation that + does not create an ExpandShapeOp (default: do not use scaling) + - use_alloc: whether to use an alloc op to allocate the temporary + tensor (default: do not use alloc op) + +#### Return modes + +This operation ignores non-Linalg ops and drops them in the return. +This operation produces a definite failure if the splitting fails for any +reason. + +If all the operations referred to by the `target` handle split +properly, the transform succeeds. Otherwise the transform produces a +silenceable failure. The 4 returned handles points to only the subset of +successfully produced computational operations, which can all be empty. +This 4 returned handles point to: + - the init op (or tensor_alloc op if use_alloc = true), + - the fill op used to initialize the neutral element, + - the split op and + - the result-combining op. + +#### Example (default: `use_scaling_algorithm = false, use_alloc = false`): + +``` + %r = linalg.generic {indexing_maps = [affine_map<(d0) -> (d0)>, + affine_map<(d0) -> ()>], + iterator_types = [\"reduction\"]} + ins(%in : tensor<32xf32>) + outs(%out : tensor) { + ^bb0(%arg1: f32, %arg2: f32): + %y = arith.addf %arg1, %arg2 : f32 + linalg.yield %y : f32 + } -> tensor +``` + +is split into: + +``` + %cst = arith.constant 0.000000e+00 : f32 + %0 = tensor.expand_shape %in [[0, 1]] : tensor<32xf32> into tensor<4x8xf32> + %1 = tensor.empty() : tensor<4xf32> + %2 = linalg.fill ins(%cst : f32) outs(%1 : tensor<4xf32>) -> tensor<4xf32> + %3 = linalg.generic {indexing_maps = [affine_map<(d0, d1) -> (d0, d1)>, + affine_map<(d0, d1) -> (d0)>], + iterator_types = [\"parallel\", \"reduction\"]} + ins(%0 : tensor<4x8xf32>) outs(%2 : tensor<4xf32>) { + ^bb0(%arg3: f32, %arg5: f32): + %5 = arith.addf %arg3, %arg4 : f32 + linalg.yield %5 : f32 + } -> tensor<4xf32> + %r = linalg.generic {indexing_maps = [affine_map<(d0) -> (d0)>, + affine_map<(d0) -> ()>], + iterator_types = [\"reduction\"]} + ins(%3 : tensor<4xf32>) outs(%out : tensor) { + ^bb0(%arg3: f32, %arg4: f32): + %5 = arith.addf %arg3, %arg4 : f32 + linalg.yield %5 : f32 + } -> tensor +``` + +#### Example (`use_scaling_algorithm = true, use_alloc = true`): + +Instead of introducing an ExpandShapeOp, this scaling-based implementation +rewrites a reduction dimension `k` into `k * split_factor + kk`. +The dimension `kk` is added as an extra parallel dimension to the +intermediate output tensor at position `insert_split_dimension`. + +Consider a minimal example where `k` is reduced: + O(i, j) += I(i, j, k) +Assume i=3, j=5, k=128, split_factor=16 and insert_split_dimension=0. +The compute is rewritten as: + a. O_i(kk, i, j) += I(i, j, 16 * k + kk) + b. O(i, j) += O_i(kk, i, j) +The intermediate tensor O_i is of shape (128/16)x3x5 == 8x3x5. + +#### Example: + +``` + %0 = linalg.matmul ins(%A, %B: tensor<16x256xf32>, tensor<256x32xf32>) + outs(%C: tensor<16x32xf32>) -> tensor<16x32xf32> +``` + +Is transformed to: + +``` + #map0 = affine_map<(d0, d1, d2, d3) -> (d0, d2 * 4 + d3)> + #map1 = affine_map<(d0, d1, d2, d3) -> (d2 * 4 + d3, d1)> + #map2 = affine_map<(d0, d1, d2, d3) -> (d2, d3)> + #map3 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> + #map4 = affine_map<(d0, d1, d2) -> (d0, d1, d2)> + #map5 = affine_map<(d0, d1, d2) -> (d0, d1)> + %0 = tensor.empty() : tensor<16x32x64xf32> + %cst = arith.constant 0.000000e+00 : f32 + %1 = linalg.fill ins(%cst : f32) outs(%0 : tensor<16x32x64xf32>) -> + tensor<16x32x64xf32> + %2 = tensor.empty() : tensor<64x4xi1> + + %3 = linalg.generic {indexing_maps = [#map0, #map1, #map2, #map3], + iterator_types = [\"parallel\", \"parallel\", \"parallel\", \"reduction\"]} + ins(%A, %B, %2 : tensor<16x256xf32>, tensor<256x32xf32>, tensor<64x4xi1>) + outs(%1 : tensor<16x32x64xf32>) { + ^bb0(%arg3: f32, %arg4: f32, %arg5: i1, %arg6: f32): + %5 = arith.mulf %arg3, %arg4 : f32 + %6 = arith.addf %arg6, %5 : f32 + linalg.yield %6 : f32 + } -> tensor<16x32x64xf32> + + %4 = linalg.generic {indexing_maps = [#map4, #map5], + iterator_types = [\"parallel\", \"parallel\", \"reduction\"]} + ins(%3 : tensor<16x32x64xf32>) + outs(%C : tensor<16x32xf32>) { + ^bb0(%arg3: f32, %arg4: f32): + %5 = arith.addf %arg3, %arg4 : f32 + linalg.yield %5 : f32 + } -> tensor<16x32xf32> + + return %4 : tensor<16x32xf32> +``` +""" +function structured_split_reduction( + target::Value; + init_or_alloc_op::IR.Type, + fill_op::IR.Type, + split_linalg_op::IR.Type, + combining_linalg_op::IR.Type, + split_factor=nothing, + insert_split_dimension=nothing, + inner_parallel=nothing, + use_scaling_algorithm=nothing, + use_alloc=nothing, + location=Location(), +) + _results = IR.Type[init_or_alloc_op, fill_op, split_linalg_op, combining_linalg_op] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(split_factor) && + push!(_attributes, namedattribute("split_factor", split_factor)) + !isnothing(insert_split_dimension) && + push!(_attributes, namedattribute("insert_split_dimension", insert_split_dimension)) + !isnothing(inner_parallel) && + push!(_attributes, namedattribute("inner_parallel", inner_parallel)) + !isnothing(use_scaling_algorithm) && + push!(_attributes, namedattribute("use_scaling_algorithm", use_scaling_algorithm)) + !isnothing(use_alloc) && push!(_attributes, namedattribute("use_alloc", use_alloc)) + + return IR.create_operation( + "transform.structured.split_reduction", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_tile_reduction_using_for` + +Indicates that the given `target` op should be transformed with the +`tileReduction` transformation with the tile size provided as attribute. + +This transformation tiles the `target` along the reduction dimensions. It +creates a tensor initialized with the identity value. Then it creates nested +loops with a parallel version of `target` op inside. The parallel op +dimensions are less or equal to the tile size passed by user. +After the loop a merge operation is created to do a final reduction with the +partial reductions. +The initial tensor always uses the tile size dimension. This may overallocate +if the tile size is greater than the reduction dimension. + +#### Return modes + +Returns 4 handles associated with (in order): + - the fill op used to initialize the neutral element, + - the parallel tiled op and + - the result-combining op, + - the parent `for` op. + +#### Example: + +``` + %red = linalg.generic {indexing_maps = [affine_map<(d0, d1) -> (d0, d1)>, + affine_map<(d0, d1) -> (d0)>], + iterator_types = [\"parallel\", \"reduction\"]} + ins(%arg0 : tensor) + outs(%out : tensor) { + ^bb0(%arg7: f32, %arg9: f32): + %1 = arith.addf %arg7, %arg9 : f32 + linalg.yield %1 : f32 + } -> tensor + return %red : tensor +``` + +is transformed into: + +``` + %0 = tensor.empty(%dim_1) : tensor + %1 = linalg.fill ins(%cst : f32) outs(%0 : tensor) -> tensor + %2 = scf.for %arg2 = %c0 to %dim_0 step %c5 iter_args(%arg3 = %1) -> (tensor) { + %extracted_slice = tensor.extract_slice %1[0, 0] [%dim, 5] [1, 1] : tensor to tensor + %extracted_slice_2 = tensor.extract_slice %arg0[0, %arg2] [%dim, 5] [1, 1] : tensor to tensor + %4 = linalg.generic {indexing_maps = [affine_map<(d0, d1) -> (d0, d1)>, + affine_map<(d0, d1) -> (d0, d1)>], + iterator_types = [\"parallel\", \"parallel\"]} + ins(%extracted_slice_2 : tensor) + outs(%extracted_slice : tensor) { + ^bb0(%in: f32, %out: f32): + %5 = arith.addf %in, %out : f32 + linalg.yield %5 : f32 + } -> tensor + %dim_3 = tensor.dim %1, %c0 : tensor + %inserted_slice = tensor.insert_slice %4 into %arg3[0, 0] [%dim_3, 5] [1, 1] : tensor into tensor + scf.yield %inserted_slice : tensor + } + %3 = linalg.generic {indexing_maps = [affine_map<(d0, d1) -> (d0, d1)>, + affine_map<(d0, d1) -> (d0)>], + iterator_types = [\"parallel\", \"reduction\"]} + ins(%2 : tensor) + outs(%arg1 : tensor) { + ^bb0(%in: f32, %out: f32): + %4 = arith.addf %in, %out : f32 + linalg.yield %4 : f32 + } -> tensor +``` +""" +function structured_tile_reduction_using_for( + target::Value; + fill_op::Vector{IR.Type}, + split_linalg_op::IR.Type, + combining_linalg_op::IR.Type, + for_op::IR.Type, + tile_sizes=nothing, + location=Location(), +) + _results = IR.Type[fill_op..., split_linalg_op, combining_linalg_op, for_op] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(tile_sizes) && push!(_attributes, namedattribute("tile_sizes", tile_sizes)) + + return IR.create_operation( + "transform.structured.tile_reduction_using_for", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_tile_reduction_using_forall` + +Tile a PartialReductionOpInterface op to a tiled `scf.forall` doing +partial reduction. + +This transformation tiles the `target` along the reduction dimensions. It +creates a tensor initialized with the identity value. Then it creates a +`scf.forall` loops with the number threads given by `num_threads`. +The op is tiled op with a size equal to `floordiv(size, num_threads)`. +All the partial reduction value is are parallel inserted to create a new +tensor. After the loop a merge operation is created to do a final reduction +with the partial reductions tensor. +If an extra `tile_sizes` parameter is passed the tiles are cyclically +distributed on the threads of the `scf.foralls` loop. + +#### Return modes + +Returns 4 handles associated with (in order): + - the fill op used to initialize the neutral element, + - the parallel tiled op and + - the result-combining op, + - the parent `forall` op. + +#### Example: + +``` + %red = linalg.generic {indexing_maps = [affine_map<(d0, d1) -> (d0, d1)>, + affine_map<(d0, d1) -> (d0)>], + iterator_types = [\"parallel\", \"reduction\"]} + ins(%arg0 : tensor) + outs(%out : tensor) { + ^bb0(%arg7: f32, %arg9: f32): + %1 = arith.addf %arg7, %arg9 : f32 + linalg.yield %1 : f32 + } -> tensor + return %red : tensor +``` + +is transformed into: + +``` + %0 = tensor.empty(%dim_1) : tensor + %1 = linalg.fill ins(%cst : f32) outs(%0 : tensor) -> tensor + %2 = scf.forall (%arg2) in (%c5) shared_outs(%arg3 = %1) -> (tensor) { + %4 = affine.min #map(%arg2)[%dim_0] + %5 = affine.max #map1(%4) + %extracted_slice = tensor.extract_slice %arg3[0, %arg2] [%dim, 1] [1, 1] : tensor to tensor + %6 = affine.apply #map2(%arg2)[%dim_0] + %extracted_slice_2 = tensor.extract_slice %arg0[0, %6] [%dim, %5] [1, 1] : tensor to tensor + %extracted_slice_3 = tensor.extract_slice %extracted_slice[0] [%dim] [1] : tensor to tensor + %7 = linalg.generic {indexing_maps = [#map3, #map4], iterator_types = [\"parallel\", \"reduction\"]} ins(%extracted_slice_2 : tensor) outs(%extracted_slice_3 : tensor) { + ^bb0(%in: f32, %out: f32): + %9 = arith.addf %in, %out : f32 + linalg.yield %9 : f32 + } -> tensor + scf.forall.in_parallel { + tensor.parallel_insert_slice %7 into %arg3[0, %arg2] [%dim, 1] [1, 1] : tensor into tensor + } + } {mapping = []} + %3 = linalg.generic {indexing_maps = [#map3, #map4], iterator_types = [\"parallel\", \"reduction\"]} ins(%2 : tensor) outs(%arg1 : tensor) { + ^bb0(%in: f32, %out: f32): + %4 = arith.addf %in, %out : f32 + linalg.yield %4 : f32 + } -> tensor +``` +""" +function structured_tile_reduction_using_forall( + target::Value; + fill_op::Vector{IR.Type}, + split_linalg_op::IR.Type, + combining_linalg_op::IR.Type, + forall_op::IR.Type, + num_threads=nothing, + tile_sizes=nothing, + mapping=nothing, + location=Location(), +) + _results = IR.Type[fill_op..., split_linalg_op, combining_linalg_op, forall_op] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(num_threads) && + push!(_attributes, namedattribute("num_threads", num_threads)) + !isnothing(tile_sizes) && push!(_attributes, namedattribute("tile_sizes", tile_sizes)) + !isnothing(mapping) && push!(_attributes, namedattribute("mapping", mapping)) + + return IR.create_operation( + "transform.structured.tile_reduction_using_forall", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_tile_using_for` + +Indicates that the given `target` op should be tiled with the given sizes. +This transform generates a loop nest with a smaller (\"tiled\") target +operation in its body. Currently limited to LinalgOps. + +Tile sizes may be known at transformation time, in which case they are +expected to be provided in the `static_size` attribute, or not, in which +case the tile value must be computed by the payload IR and the handle to the +operation computing it must be provided through `dynamic_sizes`. When the +sizes are not known statically, the corresponding entry in the +`static_sizes` attribute must be set to `ShapedType::kDynamic`. Only +the dynamic sizes must be provided in `dynamic_sizes`, i.e., there should +be as many handles as `ShapedType::kDynamic` values in the +`static_sizes` attribute. A static size of `0` indicates that the dimension +should not be tiled. No loop will be generated for such dimensions. If all +tile sizes are `0`, this transform is effectively a no-op. + +This op returns handles to the tiled op (in the generated loop nest) and the +generated loops. The number of loops is the number of tile sizes that are +statically known to be non-zero. + +#### Return modes + +On success, the resulting handles are associated with co-indexed lists of +tiled operations and loops around them. + +This operation only supports Linalg ops and produces a silenceable failure +if the input contains any non-Linalg ops. The ops preceding it in the list +associated with the `target` handle will have been tiled. + +This operation produces a silenceable failure if the `dynamic_sizes` handles +are associated with lists of payload operations of a size different than +that of the list associated with the `target` handle. + +If the internal implementation of tiling for any of the operations fails, +produces a definite failure. +""" +function structured_tile_using_for( + target::Value, + dynamic_sizes::Vector{Value}; + tiled_linalg_op::IR.Type, + loops::Vector{IR.Type}, + static_sizes=nothing, + interchange=nothing, + scalable_sizes=nothing, + location=Location(), +) + _results = IR.Type[tiled_linalg_op, loops...] + _operands = Value[target, dynamic_sizes...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(static_sizes) && + push!(_attributes, namedattribute("static_sizes", static_sizes)) + !isnothing(interchange) && + push!(_attributes, namedattribute("interchange", interchange)) + !isnothing(scalable_sizes) && + push!(_attributes, namedattribute("scalable_sizes", scalable_sizes)) + + return IR.create_operation( + "transform.structured.tile_using_for", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_tile_using_forall` + +Tile a TilingInterface op to a tiled `scf.forall`. + +Tiling is applied by either specifying `num_threads` or `tile_size`. If +`num_threads` is specified, then the tile size for each dimension `i` is +calculated dynamically via `ceilDiv(dimSize[i], num_threads[i])`. +`num_threads` and `tile_size` can be either static index attributes or +operation handles (or a mix thereof). Operation handles must be mapped to +exactly one op that has exactly one result of index type. + +Static zero tile sizes indicate that the dimension is not tiled and can be +thought of as tiling by the full size of data. + +It is the user\'s responsibility to ensure that `num_threads/tile_sizes` is +a valid tiling specification (i.e. that only tiles parallel dimensions, +e.g. in the Linalg case). If the dimension is not parallelizable, a warning +is issued to notify the user that the generated code is not safe to +parallelize. + +If non-empty, the `mapping` is added as an attribute to the +resulting `scf.forall`. + +Note: `tile_sizes` and `num_threads` are variadic. Each tile size/number of +threads can be an index attribute or a transform handle that is mapped to +exactly one payload op with exactly one index result. + +#### Return modes + +This operation ignores ops that do not implement the TilingInterface and +drops them in the return. + +If all the operations referred to by the `target` handle tile +successfully, the transform succeeds. +Otherwise the transform produces a silenceable failure. + +The two returned handles point to only the subset of successfully produced +tiled operations, which can all be empty. + +These two returned handles point to: + - the tiled op that implements TilingInterface, + - the new scf.forall op. + +#### Example using `num_threads` + +``` +%0 = transform.structured.match ops{[\"linalg.matmul\"]} in %arg1 + : (!transform.any_op) -> !transform.any_op +%3:2 = transform.structured.tile_using_forall %0 num_threads [10, 20] + : (!transform.any_op) -> (!transform.any_op, !transform.any_op) +``` + +#### Example using `tile_sizes` + +``` +%0 = transform.structured.match ops{[\"linalg.matmul\"]} in %arg1 + : (!transform.any_op) -> !transform.any_op +%sz = transform.structured.match ... +%3:2 = transform.structured.tile_using_forall %0 tile_sizes [0, %sz, 20] + : (!transform.any_op, !transform.any_op) -> (!transform.any_op, !transform.any_op) +``` +""" +function structured_tile_using_forall( + target::Value, + num_threads::Vector{Value}, + tile_sizes::Vector{Value}, + packed_num_threads=nothing::Union{Nothing,Value}; + packed_tile_sizes=nothing::Union{Nothing,Value}, + tiled_op::IR.Type, + forall_op::IR.Type, + static_num_threads=nothing, + static_tile_sizes=nothing, + mapping=nothing, + location=Location(), +) + _results = IR.Type[tiled_op, forall_op] + _operands = Value[target, num_threads..., tile_sizes...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(packed_num_threads) && push!(_operands, packed_num_threads) + !isnothing(packed_tile_sizes) && push!(_operands, packed_tile_sizes) + push!( + _attributes, + operandsegmentsizes([ + 1, + length(num_threads), + length(tile_sizes), + isnothing(packed_num_threads) ? 0 : 1, + isnothing(packed_tile_sizes) ? 0 : 1, + ]), + ) + !isnothing(static_num_threads) && + push!(_attributes, namedattribute("static_num_threads", static_num_threads)) + !isnothing(static_tile_sizes) && + push!(_attributes, namedattribute("static_tile_sizes", static_tile_sizes)) + !isnothing(mapping) && push!(_attributes, namedattribute("mapping", mapping)) + + return IR.create_operation( + "transform.structured.tile_using_forall", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_transpose_conv2d` + +Convert linalg.conv_2d_nhwc_fhwc into linalg.conv_2d_nhwc_hwcf by introducing +a linalg.transpose on the filter tensor/memref. + +Whilst the fhwc filter channel ordering can be desirable for certain targets +and is a more direct mapping to higher level dialects such as TOSA (which only +supports this ordering) hwcf is better suited for transformations such as +img2col which can make use of optimized BLAS routines such as GEMM. + +Returns one handle: +- The final operation of the sequence that replaces the original + convolution. + +#### Return modes: + +Returns a definite failure if target is not isolated from above. +Returns a silenceable failure if the pattern application failed. +""" +function structured_transpose_conv2d( + target::Value; transformed::IR.Type, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.structured.transpose_conv2d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_transpose_matmul` + +Convert Linalg matmul ops to transposed variants. + +By default the LHS matrix is transposed. Specify `` to instead +transpose RHS matrix. + +#### Return modes: + +This operation fails if `target` is unsupported, i.e., not a +`linalg.matmul` or `linalg.batch_matmul`. Otherwise, the operation succeeds +and returns a handle to the transposed matmul op. +""" +function structured_transpose_matmul( + target::Value; transformed::IR.Type, inputToTranspose=nothing, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(inputToTranspose) && + push!(_attributes, namedattribute("inputToTranspose", inputToTranspose)) + + return IR.create_operation( + "transform.structured.transpose_matmul", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_vectorize_children_and_apply_patterns` + +Vectorizes all children contained in the given `target` using the +configuration specified by the attributes of this op. This only vectorizes +structured ops that operate on shaped types and does not vectorize loops or +straight-line. Internally, it applies a set of rewrite patterns, some of +which enable vectorization and some of which clean up the results. +Therefore, it can only be applied to an op with the \"isolated from above\" +property. This transformation only fails if the entire pattern rewriting +failed, i.e., it does **not** fail when no ops were vectorized. + +Finer granularity can be achieved either with the `VectorizeOp` for +individual ops or by outlining the target part of the payload IR into, e.g., +a function, performing this transformation, and inlining it back. + +Note that this transformation invalidates the handles to any payload IR +operation that is contained inside the vectorization target. + +This transformation supports the following attributes: +- `vectorize_padding`: a `UnitAttr` to activate the vectorization of + `tensor.pad` ops. Different pipelines may prefer to lower such ops to + loops. +- `disable_multi_reduction_to_contract_patterns`: a `UnitAttr` to deactivate + the rewrite of `vector.multi_reduction` to `vector.contract`. This is + intended to be used in tests only. +- `disable_transfer_permutation_map_lowering_patterns`: a `UnitAttr` to + deactivate the rewrite of `vector.transfer` with permutation maps into + explicit `vector.transpose` operations. This is intended to be used in + tests only but may be promoted to a first class attribute in the future. + +#### Return modes: + +This operation produces a definite failure if vectorization fails for any +reason. +The operation always returns the handle to the target op that is expected +to be isolated from above. +""" +function structured_vectorize_children_and_apply_patterns( + target::Value; + transformed::IR.Type, + vectorize_padding=nothing, + vectorize_nd_extract=nothing, + flatten_1d_depthwise_conv=nothing, + disable_multi_reduction_to_contract_patterns=nothing, + disable_transfer_permutation_map_lowering_patterns=nothing, + location=Location(), +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(vectorize_padding) && + push!(_attributes, namedattribute("vectorize_padding", vectorize_padding)) + !isnothing(vectorize_nd_extract) && + push!(_attributes, namedattribute("vectorize_nd_extract", vectorize_nd_extract)) + !isnothing(flatten_1d_depthwise_conv) && push!( + _attributes, + namedattribute("flatten_1d_depthwise_conv", flatten_1d_depthwise_conv), + ) + !isnothing(disable_multi_reduction_to_contract_patterns) && push!( + _attributes, + namedattribute( + "disable_multi_reduction_to_contract_patterns", + disable_multi_reduction_to_contract_patterns, + ), + ) + !isnothing(disable_transfer_permutation_map_lowering_patterns) && push!( + _attributes, + namedattribute( + "disable_transfer_permutation_map_lowering_patterns", + disable_transfer_permutation_map_lowering_patterns, + ), + ) + + return IR.create_operation( + "transform.structured.vectorize_children_and_apply_patterns", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_vectorize` + +Vectorize the target ops, which must be Linalg ops. + +Use the optional vector sizes to specify exactly what configuration the +vectorizer should use. It will then use masked vectors of the specified +size to enforce this configuration (\"masked vectorization\"). If no vector +sizes are specified, the vectorizer will infer the shapes to use from the +target Linalg ops (\"regular vectorization\"). More specifically: + +```mlir +# Masked vectorization - vector sizes are specified explicitly +transform.structured.vectorize %target vector_sizes [1, 4] : !transform.any_op +# Regular vectorization - vector sizes are inferred from the target Op +transform.structured.vectorize %target : !transform.any_op +``` + +The vector sizes can be either static or dynamic (SSA values). In case of +SSA values, the handle must be mapped to exactly one payload op with +exactly one index-typed result. + +Note: The input vector sizes must be bigger than or equal to their +counterpart iteration space sizes. + +Typically this operator should be applied to linalg operations that have +already been tiled to the appropriate sizes. + +#### Return modes: + +This operation produces a silenceable failure if at least one target op is +not a Linalg op or fails to vectorize. It produces a definite failure if +the dynamic vector sizes (SSA values) do not satisfy the constraints +mentioned above. +""" +function structured_vectorize( + target::Value, + vector_sizes::Vector{Value}; + static_vector_sizes=nothing, + vectorize_nd_extract=nothing, + scalable_sizes=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[target, vector_sizes...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(static_vector_sizes) && + push!(_attributes, namedattribute("static_vector_sizes", static_vector_sizes)) + !isnothing(vectorize_nd_extract) && + push!(_attributes, namedattribute("vectorize_nd_extract", vectorize_nd_extract)) + !isnothing(scalable_sizes) && + push!(_attributes, namedattribute("scalable_sizes", scalable_sizes)) + + return IR.create_operation( + "transform.structured.vectorize", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`structured_winograd_conv2d` + +Winograd Conv2D algorithm will convert linalg Conv2D operation into batched +matrix multiply. Before the matrix multiply, it will convert filter and +input into a format suitable for batched matrix multiply. After the matrix +multiply, it will convert output to the final result tensor. + +The algorithm F(m x m, r x r) is + +Y = A^T x [(G x g x G^T) @ (B^T x d x B)] x A + +The size of output Y is m x m. The size of filter g is r x r. The size of +input d is (m + r - 1) x (m + r - 1). A^T, A, G^T, G, B^T, and B are +transformation matrices. + +#### Return modes: + +This operation produces a silenceable failure if `target` is unsupported. +Otherwise, the operation succeeds and returns a handle of the sequence that +replaces the original convolution. +""" +function structured_winograd_conv2d( + target::Value; transformed::IR.Type, m, r, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("m", m), namedattribute("r", r)] + + return IR.create_operation( + "transform.structured.winograd_conv2d", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`apply_patterns_memref_alloc_to_alloca` + +Collects patterns to rewrite scoped dynamic allocation (`alloc`/`dealloc` +pairs) into automatic allocation (`alloca`) in the same scope, for memrefs +of static shape. + +The `size_limit` attribute controls the maximum allocated memory (in bytes, +subject to data layout) for which the pattern applies. +""" +function apply_patterns_memref_alloc_to_alloca(; size_limit=nothing, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(size_limit) && push!(_attributes, namedattribute("size_limit", size_limit)) + + return IR.create_operation( + "transform.apply_patterns.memref.alloc_to_alloca", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_memref_expand_ops` + +Collects patterns to rewrite ops within the memref dialect. + +- Converts `atomic_rmw` that cannot be lowered to a simple atomic op with + AtomicRMWOpLowering pattern, e.g. with \"minf\" or \"maxf\" attributes, to + `memref.generic_atomic_rmw` with the expanded code. +- Converts `memref.reshape` that has a target shape of a statically-known + size to `memref.reinterpret_cast`. +""" +function apply_patterns_memref_expand_ops(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.memref.expand_ops", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_memref_expand_strided_metadata` + +Collects patterns for expanding memref operations that modify the metadata +(sizes, offset, strides) of a memref into easier to analyze constructs. +""" +function apply_patterns_memref_expand_strided_metadata(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.memref.expand_strided_metadata", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_memref_extract_address_computations` + +Collects patterns for extracting address computations from operations +with memory accesses such that these memory accesses use only a base +pointer. + +For instance, +```mlir +memref.load %base[%off0, ...] +``` + +Will be rewritten in: +```mlir +%new_base = memref.subview %base[%off0,...][1,...][1,...] +memref.load %new_base[%c0,...] +``` +""" +function apply_patterns_memref_extract_address_computations(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.memref.extract_address_computations", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_memref_fold_memref_alias_ops` + +Collects patterns for folding memref aliasing ops (memref.subview) into +consumer load/store ops (affine.load, memref.load, nvgpu.ldmatrix, +vector.load, vector.transfer_read, affine.store, memref.store, etc.) and +other ops (e.g., memref.subview). +""" +function apply_patterns_memref_fold_memref_alias_ops(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.memref.fold_memref_alias_ops", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_memref_resolve_ranked_shaped_type_result_dims` + +Collects patterns that resolve `memref.dim` operations with values that are +defined by operations that implement the `ReifyRankedShapedTypeOpInterface`, +in terms of shapes of its input operands. +""" +function apply_patterns_memref_resolve_ranked_shaped_type_result_dims(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.memref.resolve_ranked_shaped_type_result_dims", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`memref_alloca_to_global` + +Inserts a new `memref.global` for each provided `memref.alloca` into the +nearest symbol table (e.g., a `builtin.module`) and replaces it with a +`memref.get_global`. This is useful, for example, for allocations that +should reside in the shared memory of a GPU, which have to be declared as +globals. + +#### Example + +Consider the following transform op: + +```mlir +%get_global, %global = + transform.memref.alloca_to_global %alloca + : (!transform.op<\"memref.alloca\">) + -> (!transform.any_op, !transform.any_op) +``` + +and the following input payload: + +```mlir +module { + func.func @func() { + %alloca = memref.alloca() : memref<2x32xf32> + // usages of %alloca... + } +} +``` + +then applying the transform op to the payload would result in the following +output IR: + +```mlir +module { + memref.global \"private\" @alloc : memref<2x32xf32> + func.func @func() { + %alloca = memref.get_global @alloc : memref<2x32xf32> + // usages of %alloca... + } +} +``` + +#### Return modes + +Succeeds always. The returned handles refer to the `memref.get_global` and +`memref.global` ops that were inserted by the transformation. +""" +function memref_alloca_to_global( + alloca::Value; getGlobal::IR.Type, global_::IR.Type, location=Location() +) + _results = IR.Type[getGlobal, global_] + _operands = Value[alloca,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.memref.alloca_to_global", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`memref_erase_dead_alloc_and_stores` + +This applies memory optimization on memref. In particular it does store to +load forwarding, dead store elimination and dead alloc elimination. + +#### Return modes + +This operation applies a set of memory optimization on the whole region of +the operand. + +The transformation does not consume the target handle. It modifies the +payload. Dead allocations, loads and stores are silently dropped from all +mappings. +""" +function memref_erase_dead_alloc_and_stores(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.memref.erase_dead_alloc_and_stores", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`memref_make_loop_independent` + +Rewrite the targeted ops such that their index-typed operands no longer +depend on any loop induction variable of the `num_loop` enclosing `scf.for` +loops. I.e., compute an upper bound that is independent of any such loop IV +for every tensor dimension. The transformed op could then be hoisted from +the `num_loop` enclosing loops. To preserve the original semantics, place a +`memref.subview` inside the loop. + +Currently supported operations are: +- memref.alloca: Replaced with a new memref.alloca with upper bound sizes, + followed by a memref.subview. + +#### Return modes + +This operation fails if at least one induction variable could not be +eliminated. In case the targeted op is already independent of induction +variables, this transform succeeds and returns the unmodified target op. + +Otherwise, the returned handle points to a subset of the produced ops: +- memref.alloca: The returned handle points to the memref.subview op. + +This transform op consumes the target handle and produces a result handle. +""" +function memref_make_loop_independent( + target::Value; transformed::IR.Type, num_loops, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("num_loops", num_loops),] + + return IR.create_operation( + "transform.memref.make_loop_independent", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`memref_multibuffer` + +Transformation to do multi-buffering/array expansion to remove +dependencies on the temporary allocation between consecutive loop +iterations. This transform expands the size of an allocation by +a given multiplicative factor and fixes up any users of the +multibuffered allocation. +If skip analysis is not set the transformation will only apply +if it can prove that there is no data being carried across loop +iterations. + +#### Return modes + +This operation returns the new allocation if multi-buffering +succeeds, and failure otherwise. +""" +function memref_multibuffer( + target::Value; transformed::IR.Type, factor, skip_analysis=nothing, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("factor", factor),] + !isnothing(skip_analysis) && + push!(_attributes, namedattribute("skip_analysis", skip_analysis)) + + return IR.create_operation( + "transform.memref.multibuffer", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_conversion_patterns_memref_memref_to_llvm_type_converter` + +This operation provides an \"LLVMTypeConverter\" that lowers memref types to +LLVM types. + +The type converter can be customized as follows: +- `use_aligned_alloc`: Use aligned_alloc in place of malloc for heap + allocations. +- `index_bitwidth`: Bitwidth of the index type, \"0\" indicates the size of a + machine word. +- `use_generic_functions`: Use generic allocation and deallocation functions + instead of the classic \"malloc\", \"aligned_alloc\" and \"free\" functions. +// TODO: the following two options don\'t really make sense for +// memref_to_llvm_type_converter specifically. +// We should have a single to_llvm_type_converter. +- `use_bare_ptr_call_conv`: Replace FuncOp\'s MemRef arguments with bare + pointers to the MemRef element types. +- `data-layout`: String description (LLVM format) of the data layout that is + expected on the produced module. +""" +function apply_conversion_patterns_memref_memref_to_llvm_type_converter(; + use_aligned_alloc=nothing, + index_bitwidth=nothing, + use_generic_functions=nothing, + use_bare_ptr_call_conv=nothing, + data_layout=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(use_aligned_alloc) && + push!(_attributes, namedattribute("use_aligned_alloc", use_aligned_alloc)) + !isnothing(index_bitwidth) && + push!(_attributes, namedattribute("index_bitwidth", index_bitwidth)) + !isnothing(use_generic_functions) && + push!(_attributes, namedattribute("use_generic_functions", use_generic_functions)) + !isnothing(use_bare_ptr_call_conv) && + push!(_attributes, namedattribute("use_bare_ptr_call_conv", use_bare_ptr_call_conv)) + !isnothing(data_layout) && + push!(_attributes, namedattribute("data_layout", data_layout)) + + return IR.create_operation( + "transform.apply_conversion_patterns.memref.memref_to_llvm_type_converter", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`apply_conversion_patterns_nvgpu_nvgpu_to_nvvm` + +Collects patterns that convert NVGPU dialect ops to NVVM dialect ops. These +patterns require an \"LLVMTypeConverter\". +""" +function apply_conversion_patterns_nvgpu_nvgpu_to_nvvm(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_conversion_patterns.nvgpu.nvgpu_to_nvvm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`nvgpu_create_async_groups` + +Look for global to shared memory copies within the targeted op in the form +of vector transfer ops and convert them to async copies when possible. +Consecutive copies are put into the same group. A \"wait\" operation is +inserted right at the of end the group. + +`bypass_l1` specifies whether `bypassL1` attributes should be added to +the async copies. `bypass_l1` is a compiler hint: only 16 byte transfers +can bypass the L1 cache, so this attribute is not set for any other transfer +sizes. + +#### Return modes + +This op consumes the `target` handle and produces the `result` handle, which +is mapped to the same payload operations as the `target` handle. The op +modifies the payload. +""" +function nvgpu_create_async_groups( + target::Value; result::IR.Type, bypass_l1=nothing, location=Location() +) + _results = IR.Type[result,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(bypass_l1) && push!(_attributes, namedattribute("bypass_l1", bypass_l1)) + + return IR.create_operation( + "transform.nvgpu.create_async_groups", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`nvgpu_pipeline_shared_memory_copies` + +Applies software pipelining to a given scf.for loop. The pipelining +strategy will look for a load into shared memory and pipeline it to overlap +it with the rest of the loop. + +NOTE: It is user responsibility to ensure that there are no dependency +between `depth` iterations of the loop by using multi-buffering. It is +also user responsibility to ensure a sufficient amount of shared memory +is allocated to cover eventual writes by `depth-1` speculative +iterations. + +`depth` will indicate how many stages the software pipeline should have. +`peel_epilogue` allows to force the epilogue to be peeled out instead of +potentially using predicated operations for the epilogue phase. + +#### Return modes + +Consumes the operand handle and produces a result handle pointing to the +loop, which may or may not have been pipelined. Produces a definite failure +if the loop pipeliner mutated the IR before failing to pipeline, in +particular if `peel_epilogue` is not set and the loop body doesn\'t support +predication. If failure propagation mode is set to \"propagate\", produces a +silenceable failure when pipelining preconditions, e.g., loop bound being +static, are not met or when the loop wasn\'t pipelined because due to the +lack of loads into shared memory. If the failure propagation mode is set +to \"suppress\" (default), succeeds in these case and associates the result +handle with the original loop. + +TODO: the shared memory part and behavior specific to NVGPU should be +made orthogonal to pipelining so that `transform.loop.pipeline` becomes +usable here. +""" +function nvgpu_pipeline_shared_memory_copies( + for_op::Value; + result::IR.Type, + depth, + peel_epilogue=nothing, + failure_propagation_mode=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[for_op,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("depth", depth),] + !isnothing(peel_epilogue) && + push!(_attributes, namedattribute("peel_epilogue", peel_epilogue)) + !isnothing(failure_propagation_mode) && push!( + _attributes, + namedattribute("failure_propagation_mode", failure_propagation_mode), + ) + + return IR.create_operation( + "transform.nvgpu.pipeline_shared_memory_copies", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`nvgpu_rewrite_copy_as_tma` + +Rewrite a copy operation on memref to tma operations that transit through +shared memory. +""" +function nvgpu_rewrite_copy_as_tma(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.nvgpu.rewrite_copy_as_tma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`nvgpu_rewrite_matmul_as_mma_sync` + +Rewrite a matmul operation on memref to an mma.sync operation on vectors. + +Memory copies with the required access patterns are automatically inserted. +Operations that do not have a 1-1 mapping to mma.sync operations are left +unchanged. +""" +function nvgpu_rewrite_matmul_as_mma_sync(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.nvgpu.rewrite_matmul_as_mma_sync", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`apply_patterns_scf_for_loop_canonicalization` + +Collects patterns for canonicalizing operations inside SCF loop bodies. +At the moment, only affine.min/max computations with iteration variables, +loop bounds and loop steps are canonicalized. +""" +function apply_patterns_scf_for_loop_canonicalization(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.scf.for_loop_canonicalization", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_conversion_patterns_scf_structural_conversions` + +Collects patterns for performing structural conversions of SCF operations. +""" +function apply_conversion_patterns_scf_structural_conversions(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_conversion_patterns.scf.structural_conversions", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_conversion_patterns_scf_scf_to_control_flow` + +Collects patterns that lower structured control flow ops to unstructured +control flow. +""" +function apply_conversion_patterns_scf_scf_to_control_flow(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_conversion_patterns.scf.scf_to_control_flow", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop_forall_to_for` + +Converts the `scf.forall` operation pointed to by the given handle into a +set of nested `scf.for` operations. Each new operation corresponds to one +induction variable of the original \"multifor\" loop. + +The operand handle must be associated with exactly one payload operation. + +Loops with shared outputs are currently not supported. + +#### Return Modes + +Consumes the operand handle. Produces a silenceable failure if the operand +is not associated with a single `scf.forall` payload operation. +Returns as many handles as the given `forall` op has induction variables +that are associated with the generated `scf.for` loops. +Produces a silenceable failure if another number of resulting handles is +requested. +""" +function loop_forall_to_for( + target::Value; transformed::Vector{IR.Type}, location=Location() +) + _results = IR.Type[transformed...,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.loop.forall_to_for", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop_forall_to_parallel` + +Converts the `scf.forall` operation pointed to by the given handle into an +`scf.parallel` operation. + +The operand handle must be associated with exactly one payload operation. + +Loops with outputs are not supported. + +#### Return Modes + +Consumes the operand handle. Produces a silenceable failure if the operand +is not associated with a single `scf.forall` payload operation. +Returns a handle to the new `scf.parallel` operation. +Produces a silenceable failure if another number of resulting handles is +requested. +""" +function loop_forall_to_parallel( + target::Value; transformed::Vector{IR.Type}, location=Location() +) + _results = IR.Type[transformed...,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.loop.forall_to_parallel", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop_coalesce` + +Given a perfect loop nest identified by the outermost loop, +perform loop coalescing in a bottom-up one-by-one manner. + +#### Return modes + +The return handle points to the coalesced loop if coalescing happens, or +the given input loop if coalescing does not happen. +""" +function loop_coalesce(target::Value; transformed::IR.Type, location=Location()) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.loop.coalesce", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop_fuse_sibling` + +Fuses the `target` loop into the `source` loop assuming they are +independent of each other. In the fused loop, the arguments, body and +results of `target` are placed _before_ those of `source`. + +For fusion of two `scf.for` loops, the bounds and step size must match. For +fusion of two `scf.forall` loops, the bounds and the mapping must match. +Otherwise a silencable failure is produced. + +The `target` and `source` handles must refer to exactly one operation, +otherwise a definite failure is produced. It is the responsibility of the +user to ensure that the `target` and `source` loops are independent of each +other -- this op will only perform rudimentary legality checks. + +#### Return modes + +This operation consumes the `target` and `source` handles and produces the +`fused_loop` handle, which points to the fused loop. +""" +function loop_fuse_sibling( + target::Value, source::Value; fused_loop::IR.Type, location=Location() +) + _results = IR.Type[fused_loop,] + _operands = Value[target, source] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.loop.fuse_sibling", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop_outline` + +Moves the loop into a separate function with the specified name and replaces +the loop in the Payload IR with a call to that function. Takes care of +forwarding values that are used in the loop as function arguments. If the +operand is associated with more than one loop, each loop will be outlined +into a separate function. The provided name is used as a _base_ for forming +actual function names following `SymbolTable` auto-renaming scheme to avoid +duplicate symbols. Expects that all ops in the Payload IR have a +`SymbolTable` ancestor (typically true because of the top-level module). + +#### Return Modes + +Returns a handle to the list of outlined functions and a handle to the +corresponding function call operations in the same order as the operand +handle. + +Produces a definite failure if outlining failed for any of the targets. +""" +function loop_outline( + target::Value; function_::IR.Type, call::IR.Type, func_name, location=Location() +) + _results = IR.Type[function_, call] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("func_name", func_name),] + + return IR.create_operation( + "transform.loop.outline", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop_peel` + +Rewrite the given loop with a main loop and a partial (first or last) loop. +When the `peelFront` option is set as true, the first iteration is peeled off. +Otherwise, updates the given loop so that its step evenly divides its range and puts +the remaining iteration into a separate loop or a conditional. + +In the absence of sufficient static information, this op may peel a loop, +even if the step always divides the range evenly at runtime. + +#### Return modes + +This operation ignores non-scf::ForOp ops and drops them in the return. + +When `peelFront` is true, this operation returns two scf::ForOp Ops, the +first scf::ForOp corresponds to the first iteration of the loop which can +be canonicalized away in the following optimization. The second loop Op +contains the remaining iteration, and the new lower bound is the original +lower bound plus the number of steps. + +When `peelFront` is not true, this operation returns two scf::ForOp Ops, with the first +scf::ForOp satisfying: \"the loop trip count is divisible by the step\". +The second loop Op contains the remaining iteration. Note that even though the +Payload IR modification may be performed in-place, this operation consumes +the operand handle and produces a new one. + +#### Return Modes + +Produces a definite failure if peeling fails. +""" +function loop_peel( + target::Value; + peeled_loop::IR.Type, + remainder_loop::IR.Type, + peel_front=nothing, + fail_if_already_divisible=nothing, + location=Location(), +) + _results = IR.Type[peeled_loop, remainder_loop] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(peel_front) && push!(_attributes, namedattribute("peel_front", peel_front)) + !isnothing(fail_if_already_divisible) && push!( + _attributes, + namedattribute("fail_if_already_divisible", fail_if_already_divisible), + ) + + return IR.create_operation( + "transform.loop.peel", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop_pipeline` + +Transforms the given loops one by one to achieve software pipelining for +each of them. That is, performs some amount of reads from memory before the +loop rather than inside the loop, the same amount of writes into memory +after the loop, and updates each iteration to read the data for a following +iteration rather than the current one. + +The amount is specified by the attributes. + +The values read and about to be stored are transferred as loop iteration +arguments. Currently supports memref and vector transfer operations as +memory reads/writes. + +#### Return modes + +This operation ignores non-scf::For ops and drops them in the return. +If all the operations referred to by the `target` PDLOperation pipeline +properly, the transform succeeds. Otherwise the transform produces a +silenceable failure. The return handle points to only the subset of +successfully produced pipelined loops, which can be empty. +""" +function loop_pipeline( + target::Value; + transformed::IR.Type, + iteration_interval=nothing, + read_latency=nothing, + location=Location(), +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(iteration_interval) && + push!(_attributes, namedattribute("iteration_interval", iteration_interval)) + !isnothing(read_latency) && + push!(_attributes, namedattribute("read_latency", read_latency)) + + return IR.create_operation( + "transform.loop.pipeline", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop_promote_if_one_iteration` + +Promotes the given target loop op if it has a single iteration. I.e., the +loop op is removed and only the body remains. + +#### Return modes + +This transform fails if the target is mapped to ops that are loops. Ops are +considered loops if they implement the `LoopLikeOpInterface`. Otherwise, +this transform always succeeds. The transform consumes the target handle and +modifies the payload. +""" +function loop_promote_if_one_iteration(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.loop.promote_if_one_iteration", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop_unroll_and_jam` + +Unrolls & jams each loop associated with the given handle to have up to the given +number of loop body copies per iteration. If the unroll factor is larger +than the loop trip count, the latter is used as the unroll factor instead. + +#### Return modes + +This operation ignores non-`scf.for`, non-`affine.for` ops and drops them +in the return. If all the operations referred to by the `target` operand +unroll properly, the transform succeeds. Otherwise the transform produces a +silenceable failure. + +Does not return handles as the operation may result in the loop being +removed after a full unrolling. +""" +function loop_unroll_and_jam(target::Value; factor, location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("factor", factor),] + + return IR.create_operation( + "transform.loop.unroll_and_jam", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`loop_unroll` + +Unrolls each loop associated with the given handle to have up to the given +number of loop body copies per iteration. If the unroll factor is larger +than the loop trip count, the latter is used as the unroll factor instead. + +#### Return modes + +This operation ignores non-`scf.for`, non-`affine.for` ops and drops them +in the return. If all the operations referred to by the `target` operand +unroll properly, the transform succeeds. Otherwise the transform produces a +silenceable failure. + +Does not return handles as the operation may result in the loop being +removed after a full unrolling. +""" +function loop_unroll(target::Value; factor, location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("factor", factor),] + + return IR.create_operation( + "transform.loop.unroll", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`scf_take_assumed_branch` + +Given an scf.if conditional, inject user-defined information that it is +always safe to execute only the if or else branch. + +This is achieved by just replacing the scf.if by the content of one of its +branches. + +This is particularly useful for user-controlled rewriting of conditionals +that exist solely to guard against out-of-bounds behavior. + +At the moment, no assume or assert operation is emitted as it is not always +desirable. In the future, this may be controlled by a dedicated attribute. + +#### Return modes + +The transform only consumes its operand and does not produce any result. +The transform definitely fails if `take_else_branch` is specified and the +`else` region is empty. +""" +function scf_take_assumed_branch( + target::Value; take_else_branch=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(take_else_branch) && + push!(_attributes, namedattribute("take_else_branch", take_else_branch)) + + return IR.create_operation( + "transform.scf.take_assumed_branch", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`sparse_tensor_match_sparse_inout` + +Checks if the payload op has any sparse inputs and/or outputs. +""" +function sparse_tensor_match_sparse_inout( + target::Value; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.sparse_tensor.match.sparse_inout", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`apply_patterns_tensor_decompose_concat` + +Indicates that tensor.concat ops should be decomposed into a chain of +tensor.insert_slice operations inserting into a materialized destination. +""" +function apply_patterns_tensor_decompose_concat(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.tensor.decompose_concat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_tensor_drop_redundant_insert_slice_rank_expansion` + +Indicates that redundant tensor.insert_slice rank reductions should be +dropped. E.g., cases where a tensor.extract_slice rank reduction immediately +follows an inverse tensor.insert_slice rank expansion. +""" +function apply_patterns_tensor_drop_redundant_insert_slice_rank_expansion(; + location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.tensor.drop_redundant_insert_slice_rank_expansion", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_tensor_fold_into_pack_and_unpack` + +Indicates that operations like tensor.pad and tensor.extract_slice should +be folded into tensor.pack and tensor.unpack operations, respectively. +""" +function apply_patterns_tensor_fold_into_pack_and_unpack(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.tensor.fold_into_pack_and_unpack", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_tensor_fold_tensor_empty` + +Indicates that tensor.extract_slice and reassociative reshapes should be +folded into tensor.empty. + +If `fold_single_use_only` is set to \"true\", only tensor.empty that have a +single use are folded. +""" +function apply_patterns_tensor_fold_tensor_empty(; + fold_single_use_only=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(fold_single_use_only) && + push!(_attributes, namedattribute("fold_single_use_only", fold_single_use_only)) + + return IR.create_operation( + "transform.apply_patterns.tensor.fold_tensor_empty", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_tensor_fold_tensor_subset_ops_into_vector_transfers` + +Indicates that tensor.extract_slice -> vector.transfer_read and +vector.transfer_write -> tensor.insert_slice op chains should be folded into +vector tranfer read and write ops +""" +function apply_patterns_tensor_fold_tensor_subset_ops_into_vector_transfers(; + location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.tensor.fold_tensor_subset_ops_into_vector_transfers", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_tensor_fold_tensor_subset_ops` + +Indicates that tensor.empty should be folded with tensor.extract_slice, +tensor.expand_shape and tensor.collapse_shape. +""" +function apply_patterns_tensor_fold_tensor_subset_ops(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.tensor.fold_tensor_subset_ops", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_tensor_merge_consecutive_insert_extract_slice` + +Indicates that consecutive tensor.extract_slice/tensor.insert_slice ops +should be merged into a single op. These patterns are not canonicalizations +because the bufferization is sensitive to IR structure. +""" +function apply_patterns_tensor_merge_consecutive_insert_extract_slice(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.tensor.merge_consecutive_insert_extract_slice", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_tensor_reassociative_reshape_folding` + +Indicates that reassociative reshapes (tensor.collapse_shape / +tensor.expand_shape) should be folded with inverse rank expansions / rank +reductions (via tensor.insert_slice / tensor.extract_slice). +""" +function apply_patterns_tensor_reassociative_reshape_folding(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.tensor.reassociative_reshape_folding", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_tensor_rewrite_as_constant` + +Indicates that tensor ops (such as tensor.generate) should be replaced with +constants (arith.constant) when possible. +""" +function apply_patterns_tensor_rewrite_as_constant(; + aggressive=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(aggressive) && push!(_attributes, namedattribute("aggressive", aggressive)) + + return IR.create_operation( + "transform.apply_patterns.tensor.rewrite_as_constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`tensor_make_loop_independent` + +Rewrite the targeted ops such that their index-typed operands no longer +depend on any loop induction variable of the `num_loop` enclosing `scf.for` +loops. I.e., compute an upper bound that is independent of any such loop IV +for every tensor dimension. The transformed op could then be hoisted from +the `num_loop` enclosing loops. To preserve the original semantics, place a +`tensor.extract_slice` inside the loop. + +Currently supported operations are: +- tensor.empty: Replaced with a new tensor.empty with upper bound sizes, + followed by a tensor.extract_slice. +- tensor.pad: Replaced by an upper bound padding, followed by a + tensor.extract_slice. + +#### Return modes + +This operation fails if at least one induction variable could not be +eliminated. In case the targeted op is already independent of induction +variables, this transform succeeds and returns the unmodified target op. + +Otherwise, the returned handle points to a subset of the produced ops: +- tensor.empty: The returned handle points to the tensor.extract_slice op. +- tensor.pad: The returned handle points to the tensor.extract_slice op. + +This transform op consumes the target handle and produces a result handle. +""" +function tensor_make_loop_independent( + target::Value; transformed::IR.Type, num_loops, location=Location() +) + _results = IR.Type[transformed,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("num_loops", num_loops),] + + return IR.create_operation( + "transform.tensor.make_loop_independent", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`type_conversion_tensor_cast_shape_dynamic_dims` + +Populates a type converter with conversion materialization functions that +cast a tensor value between two cast-compatible tensors. See `tensor.cast` +for more information on cast compatibility between tensors. + +If `ignore_dynamic_info` is not set, this will set an additional constraint +that source materializations do not cast dynamic dimensions to static ones. +""" +function type_conversion_tensor_cast_shape_dynamic_dims(; + ignore_dynamic_info=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(ignore_dynamic_info) && + push!(_attributes, namedattribute("ignore_dynamic_info", ignore_dynamic_info)) + + return IR.create_operation( + "transform.type_conversion.tensor.cast_shape_dynamic_dims", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`alternatives` + +This op may have an arbitrary number of regions, each of which represents a +sequence of transform operations to be applied to the same payload IR. The +regions are visited in order of appearance, and transforms in them are +applied in their respective order of appearance. If one of these transforms +fails to apply, the remaining ops in the same region are skipped an the next +region is attempted. If all transformations in a region succeed, the +remaining regions are skipped and the entire \"alternatives\" transformation +succeeds. If all regions contained a failing transformation, the entire +\"alternatives\" transformation fails. + +It is up to the nested operations to define which errors are \"recoverable\" +(or \"silenceable\") and allow another alternatives to be attempted, and which +errors should be propagated without attempting the other alternatives. + +The single operand of this operation is the scope in which the alternative +transformation sequences are attempted, that is, an operation in the payload +IR that contains all the other operations that may be modified by the +transformations. The scope operation must be isolated from above. There is +no check that the transforms are indeed scoped as their \"apply\" methods can +be arbitrarily complex. Therefore it is the responsibility of the user to +ensure that the transforms are scoped correctly, or to produce an +irrecoverable error and thus abort the execution without attempting the +remaining alternatives. Note that the payload IR outside of the given scope +is not necessarily in the valid state, or even accessible to the +transformation. + +The changes to the IR within the scope performed by transforms in the failed +alternative region are reverted before attempting the next region. +Practically, this is achieved by cloning the scope. Therefore it is advised +to limit the scope as much as possible and place the most likely +alternatives early in the region list. The operation is also isolated from +above and requires rediscovering the operations within the given scope to +avoid additional handle invalidation. The latter restriction may be lifted +in the future. + +Each of the regions may yield transform IR handles. The handles of the first +successful alternative region are returned as the results of the +\"alternatives\" op. Therefore, each alternative region must yield the same +number of results, which should also match the number and the types of the +\"alternatives\" op results. + +Remark: this op allows one to implement a simple \"try\" construct as follows: + +```mlir +%result = transform.alternatives %scope { +^bb0(%arg0: !transform.any_op): + // Try a fallible transformation. + %0 = transform.fallible %arg0 // ... + // If succeeded, yield the the result of the transformation. + transform.yield %0 : !transform.any_op +}, { +^bb0(%arg0: !transform.any_op): + // Otherwise, the second alternative is tried and it always succeeds by + // returning the original handle. + transform.yield %arg0 : !transform.any_op +} +``` +""" +function alternatives( + scope=nothing::Union{Nothing,Value}; + results::Vector{IR.Type}, + alternatives::Vector{Region}, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[] + _owned_regions = Region[alternatives...,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(scope) && push!(_operands, scope) + + return IR.create_operation( + "transform.alternatives", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`annotate` + +Adds an attribute with the given `name` to the `target` operation. An +optional `param` handle can be provided to give the attribute a specific +value, else a UnitAttr is added. A single attribute will be broadcasted to +all target operations, otherwise the attributes will be mapped 1:1 based on +the order within the handles. + +Produces a silenceable failure if the length of the parameter payload does +not match the length of the target payload. Does not consume the provided +handles. +""" +function annotate( + target::Value, param=nothing::Union{Nothing,Value}; name, location=Location() +) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("name", name),] + !isnothing(param) && push!(_operands, param) + + return IR.create_operation( + "transform.annotate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_canonicalization` + +This op populates all canonicalization patterns of all loaded dialects in +an `apply_patterns` transform. +""" +function apply_patterns_canonicalization(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.canonicalization", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_cse` + +This transform applies common subexpression elimination (CSE) to the body +of the targeted op. + +This transform reads the target handle and modifies the payload. Existing +handles to operations inside of the targeted op are retained and updated if +necessary. Note that this can lead to situations where a handle, that was +previously mapped to multiple distinct (but equivalent) operations, is now +mapped to the same operation multiple times. +""" +function apply_cse(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_cse", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_conversion_patterns` + +This transform applies the specified conversion patterns to the targeted op +and all nested ops. By default, this transform applies a \"full\" dialect +conversion. If the `partial_conversion` unit attribute is present, this +transform applies a partial dialect conversion. + +The patterns that should be applied are specified in the first graph region +of this op. They must implement the +`ConversionPatternDescriptorOpInterface`. The order in which patterns are +applied is unspecified; i.e., the ordering of ops in the region of this op +is irrelevant. + +The second, optional graph region contains exactly one op that specifies +default type converter that should be used with this dialect conversion. If +provided, this op must implement the `TypeConverterBuilderOpInterface`. +Type converters are a property of conversion patterns: each conversion +pattern stores the type converter that should be used in its C++ class. Each +conversion pattern descriptor can optionally specify a type converter in its +`getTypeConverter` interface method. If no type converter is specified in +this method, the default type converter of the dialect conversion is used. +Default type converters are useful if the same type converter should be used +for multiple sets of conversion patterns. (Patterns that should not use this +default type converter specify their own type converter.) + +The `legal_ops`, `illegal_ops`, `legal_dialects`, `illegal_dialects` +attributes specify the conversion target. + +This transform modifies the payload. By default, it consumes the `target` +handle. It does not produce any handles. + +If the `preserve_handles` attribute is set, this transform does not consume +the `target` handle and instead updates handles based on notifications from +a tracking listener that is attached to the dialect conversion, similar to +`transform.apply_patterns`. Only replacements via `RewriterBase::replaceOp` +or `replaceOpWithNewOp` are considered \"payload op replacements\". In +contrast to `transform.apply_patterns`, we allow replacement ops even if the +op name has changed. This is because conversion patterns are expected to +lower ops to different ops (from a different dialect). More details can be +found at the documentation site of `TrackingListener`. + +This transform produces a silenceable failure if the dialect conversion was +unsuccessful or the tracking listener failed to find a replacement op. +""" +function apply_conversion_patterns( + target::Value; + legal_ops=nothing, + illegal_ops=nothing, + legal_dialects=nothing, + illegal_dialects=nothing, + partial_conversion=nothing, + preserve_handles=nothing, + patterns::Region, + default_type_converter_region::Vector{Region}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[patterns, default_type_converter_region...] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(legal_ops) && push!(_attributes, namedattribute("legal_ops", legal_ops)) + !isnothing(illegal_ops) && + push!(_attributes, namedattribute("illegal_ops", illegal_ops)) + !isnothing(legal_dialects) && + push!(_attributes, namedattribute("legal_dialects", legal_dialects)) + !isnothing(illegal_dialects) && + push!(_attributes, namedattribute("illegal_dialects", illegal_dialects)) + !isnothing(partial_conversion) && + push!(_attributes, namedattribute("partial_conversion", partial_conversion)) + !isnothing(preserve_handles) && + push!(_attributes, namedattribute("preserve_handles", preserve_handles)) + + return IR.create_operation( + "transform.apply_conversion_patterns", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_dce` + +This transform applies dead code elimination (DCE) to the body of the +targeted op. + +Note: \"transform.apply_patterns\" with an empty region can also be used to +remove dead ops. However, that op applies additional simplifications such as +op folding and region simplification. + +This transform reads the target handle and modifies the payload. Note that +this transform may silently remove payload ops from handles. +""" +function apply_dce(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_dce", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_licm` + +This transform moves side-effect free, loop invariant code out of the +targeted loop-like op. The targeted op must implement the +`LoopLikeOpInterface`. + +Note: To move invariant ops from a loop nest, this transform must be applied +to each loop of the loop nest, starting with the inner-most loop. + +This transform reads the target handle and modifies the payload. +""" +function apply_licm(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_licm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns` + +This transform greedily applies the specified patterns to the body of the +targeted op until a fixpoint was reached. Patterns are not applied to the +targeted op itself. + +The patterns that should be applied are specified in the graph region of +this op. They must implement the `PatternDescriptorOpInterface`. The order +in which patterns are applied is unspecified; i.e., the ordering of ops in +the region of this op is irrelevant. + +If `apple_cse` is set, the greedy pattern rewrite is interleaved with +common subexpression elimination (CSE): both are repeated until a fixpoint +is reached. + +This transform only reads the target handle and modifies the payload. If a +pattern erases or replaces a tracked op, the mapping is updated accordingly. + +Only replacements via `RewriterBase::replaceOp` or `replaceOpWithNewOp` are +considered \"payload op replacements\". Furthermore, only if the replacement +values are defined by the same op and that op has the same type as the +original op, the mapping is updated. Otherwise, this transform produces a +silenceable failure. More details can be found at the documentation site of +`TrackingListener`. + +This transform also produces a silenceable failure if the pattern +application did not converge within the default number of +iterations/rewrites of the greedy pattern rewrite driver. +""" +function apply_patterns( + target::Value; + apply_cse=nothing, + max_iterations=nothing, + max_num_rewrites=nothing, + patterns::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[patterns,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(apply_cse) && push!(_attributes, namedattribute("apply_cse", apply_cse)) + !isnothing(max_iterations) && + push!(_attributes, namedattribute("max_iterations", max_iterations)) + !isnothing(max_num_rewrites) && + push!(_attributes, namedattribute("max_num_rewrites", max_num_rewrites)) + + return IR.create_operation( + "transform.apply_patterns", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_registered_pass` + +This transform applies the specified pass or pass pipeline to the targeted +ops. The name of the pass/pipeline is specified as a string attribute, as +set during pass/pipeline registration. Optionally, pass options may be +specified as a string attribute. The pass options syntax is identical to the +one used with \"mlir-opt\". + +This op first looks for a pass pipeline with the specified name. If no such +pipeline exists, it looks for a pass with the specified name. If no such +pass exists either, this op fails definitely. + +This transform consumes the target handle and produces a new handle that is +mapped to the same op. Passes are not allowed to remove/modify the operation +that they operate on, so the target op is guaranteed to still exist. The +target handle is invalidated because a pass may arbitrarily modify the body +of targeted ops. +""" +function apply_registered_pass( + target::Value; result::IR.Type, pass_name, options=nothing, location=Location() +) + _results = IR.Type[result,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("pass_name", pass_name),] + !isnothing(options) && push!(_attributes, namedattribute("options", options)) + + return IR.create_operation( + "transform.apply_registered_pass", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_conversion_patterns_dialect_to_llvm` + +Collects patterns that convert ops from the specified dialect to LLVM +dialect ops. These patterns require an \"LLVMTypeConverter\". + +Note: Only dialects that implement the `ConvertToLLVMPatternInterface` are +supported. Any conversion target modifications by interface implementations +are currently ignored. The conversion target is fully specified by the +enclosing \"apply_conversion_patterns\" op. +""" +function apply_conversion_patterns_dialect_to_llvm(; dialect_name, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("dialect_name", dialect_name),] + + return IR.create_operation( + "transform.apply_conversion_patterns.dialect_to_llvm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`cast` + +""" +function cast(input::Value; output::IR.Type, location=Location()) + _results = IR.Type[output,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`collect_matching` + +Collects operations or other payload IR objects nested under `root` +(inclusive) that match the given matcher expressed as a named sequence. The +matcher sequence must accept exactly one argument that it is not allowed to +modify. It must yield as many values as this op has results. Each of the +yielded values must be associated with exactly one payload object. If any +operation in the matcher sequence produces a silenceable failure, the +matcher advances to the next payload operation in the walk order without +finishing the sequence. + +The i-th result of this operation is constructed by concatenating the i-th +yielded payload IR objects of all successful matcher sequence applications. +All results are guaranteed to be mapped to the same number of payload IR +objects. + +The operation succeeds unless the matcher sequence produced a definite +failure for any invocation. +""" +function collect_matching( + root::Value; results::Vector{IR.Type}, matcher, location=Location() +) + _results = IR.Type[results...,] + _operands = Value[root,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("matcher", matcher),] + + return IR.create_operation( + "transform.collect_matching", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`foreach_match` + +Given a pair of co-indexed lists of transform dialect symbols (such as +`transform.named_sequence`), walks the payload IR associated with the root +handle and interprets the symbols as matcher/action pairs by applying the +body of the corresponding symbol definition. The symbol from the first list +is the matcher part: if it results in a silenceable error, the error is +silenced and the next matcher is attempted. Definite failures from any +matcher stop the application immediately and are propagated unconditionally. +If none of the matchers succeeds, the next payload operation in walk order +(post-order at the moment of writing, double check `Operation::walk`) is +matched. If a matcher succeeds, the co-indexed action symbol is applied and +the following matchers are not applied to the same payload operation. If the +action succeeds, the next payload operation in walk order is matched. If it +fails, both silenceable and definite errors are propagated as the result of +this op; propagation of silenceable errors is postponed until the end of the +walk. + +The matcher symbol must take at least one operand of a type that implements +the same transform dialect interface as the `root` operand (a check is +performed at application time to see if the associated payload satisfies the +constraints of the actual type), and may take additional operands with a +similar type requirement. It must not consume operands as multiple matchers +may be applied. The matcher may produce any number of results. The action +symbol paired with the matcher must take the same number of arguments as the +matcher has results, and these arguments must implement the same transform +dialect interfaces, but not necessarily have the exact same type (again, a +check is performed at application time to see if the associated payload +satisfies the constraints of actual types on both sides). + +The action symbol may have results that are accumulated from all actions and +returned from the `foreach_match` operation on success. Unless the +`flatten_results` attribute is present, each action result must be +associated with exactly one payload entity. The actions are expected to only +modify payload operations nested in the `root` payload operations associated +with the operand of this transform operation. Furthermore, the actions may +not modify operations outside of the currently matched payload operation, +e.g., they may not modify sibling or parent operations. If such behavior is +desired, the parent must be matched first and the nested operations obtained +by traversing the IR from the parent. This is due to the matching being +performed as a post-order IR walk. + +This operation consumes the operand and produces a new handle associated +with the same payload. This is necessary to trigger invalidation of handles +to any of the payload operations nested in the payload operations associated +with the operand, as those are likely to be modified by actions. + +By default, the root payload operation associated with the operand is not +matched. This is to support the conservative case where applied actions may +invalidate the root payload operation. If the optional `restrict_root` +attribute is set, the root operand is guaranteed to not be invalidated by any +of the applied actions. In such cases, the root payload operation is also +matched. This is useful because matching the root payload operation is a +common idiom, when e.g. matching a func.func directly and operations nested +under it. + +The operation succeeds if none of the matchers produced a definite failure +during application and if all of the applied actions produced success. Note +that it also succeeds if all the matchers failed on all payload operations, +i.e. failure to apply is not an error. The operation produces a silenceable +failure if any applied action produced a silenceable failure. In this case, +the resulting handle is associated with an empty payload. The operation +produces a definite failure if any of the applied matchers or actions +produced a definite failure. +""" +function foreach_match( + root::Value, + forwarded_inputs::Vector{Value}; + updated::IR.Type, + forwarded_outputs::Vector{IR.Type}, + restrict_root=nothing, + flatten_results=nothing, + matchers, + actions, + location=Location(), +) + _results = IR.Type[updated, forwarded_outputs...] + _operands = Value[root, forwarded_inputs...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("matchers", matchers), namedattribute("actions", actions) + ] + !isnothing(restrict_root) && + push!(_attributes, namedattribute("restrict_root", restrict_root)) + !isnothing(flatten_results) && + push!(_attributes, namedattribute("flatten_results", flatten_results)) + + return IR.create_operation( + "transform.foreach_match", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`foreach` + +Execute the op\'s body - its single region block - exactly once per +element of the payload associated to a target handle. The body\'s +transformations are applied in order of appearance until reaching the +(implicit) YieldOp terminator. + +Each iteration gets executed by co-indexing the payloads of the arguments +and mapping the body\'s arguments to these tuples, as though iterating over +the zipped together `targets`. As such, in each iteration, the size of the +payload of each of the body\'s block arguments is exactly one. The attribute +`zip_shortest` can be used if the targets vary in their number of payloads; +this will limit the iterations to only the number of payloads found in the +shortest target. + +This op always reads the target handles. Furthermore, it consumes a handle +if there is a transform op in the body that consumes the corresponding +block argument. Handles can point to ops, values, or parameters. + +#### Return Modes + +This op produces as many result handles as the body\'s terminating YieldOp +has operands. For each result, the payloads of the corresponding YieldOp +operand are merged and mapped to the same resulting handle. + +If the target handles do not associate payloads of the same size, a +silencable failure will be generated. + +During application, if any transformation in the sequence fails, the entire +sequence fails immediately with the same failure, leaving the payload IR in +a potentially invalid state, i.e., this operation offers no transformation +rollback capabilities. +""" +function foreach( + targets::Vector{Value}; + results::Vector{IR.Type}, + with_zip_shortest=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[targets...,] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(with_zip_shortest) && + push!(_attributes, namedattribute("with_zip_shortest", with_zip_shortest)) + + return IR.create_operation( + "transform.foreach", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_consumers_of_result` + +The handle defined by this Transform op corresponds to all operations that +consume the SSA value defined by the `target` and `result_number` +arguments. +This operation applies to a single payload operation, otherwise it produces +a definite failure. +The return handle points to the consuming operations operations, which can +be empty. +""" +function get_consumers_of_result( + target::Value; consumers::IR.Type, result_number, location=Location() +) + _results = IR.Type[consumers,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("result_number", result_number),] + + return IR.create_operation( + "transform.get_consumers_of_result", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_defining_op` + +The handle defined by this Transform op corresponds to the defining op of +the targeted value. + +This transform produces a silenceable failure if the targeted value is a +block argument. +""" +function get_defining_op(target::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.get_defining_op", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_operand` + +The handle defined by this Transform op corresponds to the operands of the +given `target` operation specified by the given set of positions. There are +three possible modes: + + - Position list directly, i.e. `%target[0, 1, 2]`. This will return the + operands at the specified positions. + - Inverted position list, i.e. `%target[except(0, 1, 2)]`. This will return + all operands except those at the given positions. + - All, i.e. `%target[all]`. This will return all operands of the operation. + +This transform produces a silenceable failure if any of the operand indices +exceeds the number of operands in the target. It reads the target handle and +produces the result handle. +""" +function get_operand( + target::Value; + result::IR.Type, + raw_position_list, + is_inverted=nothing, + is_all=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("raw_position_list", raw_position_list),] + !isnothing(is_inverted) && + push!(_attributes, namedattribute("is_inverted", is_inverted)) + !isnothing(is_all) && push!(_attributes, namedattribute("is_all", is_all)) + + return IR.create_operation( + "transform.get_operand", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_parent_op` + +The handle defined by this Transform op corresponds to the parents of the +targeted payload ops (in the same order). + +Requirements that parent ops must fulfill can be optionally specified. In +that case for each target op, the closest parent op that fulfills all +requirements, is returned. +- `isolated_from_above`: the parent op must be isolated from above +- `allow_empty_results`: get_parent_op is allowed to return an empty list + and still succeeds. In such a case, if `get_parent_op` fails for any + operation in the list, the entire transform returns an empty handle. +- `op_name`: the parent op must have the specified name +- `nth_parent`: get the n-th parent of that satisfies the above requirements + +If `deduplicate` is set, the result handle does not contain any duplicate +ops. For example, given the list +\"(childof(A), childof(B), childof(B), childof(A), childof(B))\", the +resulting list will be just \"(A, B)\". Note that no other semantic ordering +is applied, e.g., \"B\" may itself be a parent of \"A\". This may have an impact +on the further transformation applied to the handle produced here. + +If any of the given Payload IR ops has no such suitable parent, then: + - if `allow_empty_results` is set, the result handle is empty + - otherwise, the transformation produces a silenceable failure. +""" +function get_parent_op( + target::Value; + parent::IR.Type, + isolated_from_above=nothing, + allow_empty_results=nothing, + op_name=nothing, + deduplicate=nothing, + nth_parent=nothing, + location=Location(), +) + _results = IR.Type[parent,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(isolated_from_above) && + push!(_attributes, namedattribute("isolated_from_above", isolated_from_above)) + !isnothing(allow_empty_results) && + push!(_attributes, namedattribute("allow_empty_results", allow_empty_results)) + !isnothing(op_name) && push!(_attributes, namedattribute("op_name", op_name)) + !isnothing(deduplicate) && + push!(_attributes, namedattribute("deduplicate", deduplicate)) + !isnothing(nth_parent) && push!(_attributes, namedattribute("nth_parent", nth_parent)) + + return IR.create_operation( + "transform.get_parent_op", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_producer_of_operand` + +The handle defined by this Transform op corresponds to operation that +produces the SSA value defined by the `target` and `operand_number` +arguments. If the origin of the SSA value is not an operations (i.e. it is +a block argument), the transform produces a silenceable failure. +The return handle points to only the subset of successfully produced +computational operations, which can be empty. +""" +function get_producer_of_operand( + target::Value; producer::IR.Type, operand_number, location=Location() +) + _results = IR.Type[producer,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("operand_number", operand_number),] + + return IR.create_operation( + "transform.get_producer_of_operand", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_result` + +The handle defined by this Transform op correspond to the OpResults of the +given `target` operation. Optionally `result_number` can be specified to +select a specific result. + +This transform fails silently if the targeted operation does not have enough +results. It reads the target handle and produces the result handle. + +The handle defined by this Transform op corresponds to the results of the +given `target` operation specified by the given set of positions. There are +three possible modes: + + - Position list directly, i.e. `%target[0, 1, 2]`. This will return the + results at the specified positions. + - Inverted position list, i.e. `%target[except(0, 1, 2)]`. This will return + all results except those at the given positions. + - All, i.e. `%target[all]`. This will return all results of the operation. + +This transform produces a silenceable failure if any of the result indices +exceeds the number of results returned by the target. It reads the target +handle and produces the result handle. +""" +function get_result( + target::Value; + result::IR.Type, + raw_position_list, + is_inverted=nothing, + is_all=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("raw_position_list", raw_position_list),] + !isnothing(is_inverted) && + push!(_attributes, namedattribute("is_inverted", is_inverted)) + !isnothing(is_all) && push!(_attributes, namedattribute("is_all", is_all)) + + return IR.create_operation( + "transform.get_result", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`get_type` + +This operation creates a new Transform parameter containing the +type(s) of the value(s) associated with the operand handle. + +This transform never fails. +""" +function get_type(value::Value; type_param::IR.Type, elemental=nothing, location=Location()) + _results = IR.Type[type_param,] + _operands = Value[value,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(elemental) && push!(_attributes, namedattribute("elemental", elemental)) + + return IR.create_operation( + "transform.get_type", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`include_` + +The application of this transform operation is equivalent to applying the +operations contained in the named transform sequence with operands being +remapped to block arguments. The behavior of the operation when a +transformation in the included named sequence produces a silenceable error +is controlled by the `failure_propagation_mode` attribute. When set to +`propagate`, the failure of any nested transformation in the sequence +implies immediate failure of the entire sequence with a silenceable error, +and no further transformation is attempted. When set to `suppress`, +silenceable errors in nested operations are ignored and further +transformations are applied. Beware that even silenceable errors may leave +the payload IR in a state unsuitable for further transformations. It is the +responsibility of the user to ensure the following transformations are +robust enough when errors are suppressed. Definite errors are propagated +immediately regardless of the mode. The objects associated with the results +of this operation are the same as those associated with the operands of the +`transform.yield` in the referenced named sequence. +""" +function include_( + operands::Vector{Value}; + results::Vector{IR.Type}, + target, + failure_propagation_mode, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("target", target), + namedattribute("failure_propagation_mode", failure_propagation_mode), + ] + + return IR.create_operation( + "transform.include", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_operation_empty` + +Succeeds if the handle is not associated to any op. +""" +function match_operation_empty(operand_handle::Value; location=Location()) + _results = IR.Type[] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.match.operation_empty", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_operation_name` + +Succeeds if the operation associated with the operand handle has one of the +given operation names. Produces a silenceable failure otherwise. + +If more than one payload operation is associated with the operand handle, +produces a definite failure. +""" +function match_operation_name(operand_handle::Value; op_names, location=Location()) + _results = IR.Type[] + _operands = Value[operand_handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("op_names", op_names),] + + return IR.create_operation( + "transform.match.operation_name", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`match_param_cmpi` + +Succeeds if all of the co-indexed values associated with the given +parameters relate as specified by the predicate (greater than, less than, +equal to, or their combinations). Comparison treats all values as signed. +Produces a silenceable failure otherwise. +""" +function match_param_cmpi(param::Value, reference::Value; predicate, location=Location()) + _results = IR.Type[] + _operands = Value[param, reference] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("predicate", predicate),] + + return IR.create_operation( + "transform.match.param.cmpi", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`merge_handles` + +Creates a new Transform IR handle value that points to the same Payload IR +operations/values/parameters as the operand handles. The Payload IR elements +are listed in the same order as they are in the operand handles, grouped by +operand handle, e.g., all Payload IR associated with the first handle comes +first, then all Payload IR associated with the second handle and so on. If +`deduplicate` is set, do not add the given Payload IR operation, value, or +parameter more than once to the final list regardless of it coming from the +same or different handles. Consumes the operands and produces a new handle. +""" +function merge_handles( + handles::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + deduplicate=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[handles...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + !isnothing(deduplicate) && + push!(_attributes, namedattribute("deduplicate", deduplicate)) + + return IR.create_operation( + "transform.merge_handles", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`named_sequence` + +Defines a named (callable, function-like) sequence of other Transform +dialect operations that can be included using `transform.include` as part of +another Transform dialect construct. This sequence is not processed +immediately but rather dispatched to when the inclusion is processed. The +arguments and results can be used to communicate a subset of mapping into +the named sequence. The sequence must consist of a single block and end with +a `transform.yield` terminator. The operands of the terminator become the +results of the `transform.include`. + +When dispatched to, the operations in the named sequence are executed one by +one, similarly to the regular unnamed sequence. The failure propagation mode +is specified on the `transform.include`. Different inclusions may use +different failure propagation modes. This transform operation always +succeeds by itself, but the inclusion may fail if any of the operations +fail. + +Named sequences can only appear at the top-level of the Transform dialect +nesting structure. That is, they cannot be nested in other Transform dialect +operations. Furthermore, one of the ancestors must have the `SymbolTable` +trait and have the `transform.with_named_sequence` attribute attached. + +Named sequences may include other named sequences via `transform.include`, +but recursion is *not* allowed. +""" +function named_sequence(; + sym_name, + function_type, + sym_visibility=nothing, + arg_attrs=nothing, + res_attrs=nothing, + body::Region, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("sym_name", sym_name), namedattribute("function_type", function_type) + ] + !isnothing(sym_visibility) && + push!(_attributes, namedattribute("sym_visibility", sym_visibility)) + !isnothing(arg_attrs) && push!(_attributes, namedattribute("arg_attrs", arg_attrs)) + !isnothing(res_attrs) && push!(_attributes, namedattribute("res_attrs", res_attrs)) + + return IR.create_operation( + "transform.named_sequence", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`num_associations` + +Given an argument, handle or parameter, returns a new parameter associated +with a single 64-bit number that corresponds to the number of payload +objects (operations or values for a handle, attributes for a parameter) +associated with the argument. + +Always succeeds. +""" +function num_associations(handle::Value; num::IR.Type, location=Location()) + _results = IR.Type[num,] + _operands = Value[handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.num_associations", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`param_constant` + +Produces a new transform dialect parameter associated with the singleton +list containing the given attribute. The operation itself always succeeds, +but the general association check may fail if the parameter type does not +accept the given kind of attribute as valid. +""" +function param_constant(; param::IR.Type, value, location=Location()) + _results = IR.Type[param,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("value", value),] + + return IR.create_operation( + "transform.param.constant", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`print` + +Prints each payload op that is associated with the `target` operand to +`stdout`. It also prints the `name` string attribute. If no target is +specified, the top-level op is dumped. + +This op is useful for printf-style debugging. + +Supported printing flag attributes: +* `assume_verified` -- skips verification when the unit attribute is + specified. This improves performace but may lead to crashes and + unexpected behavior when the printed payload op is invalid. +* `use_local_scope` -- prints in local scope when the unit attribute is + specified. This improves performance but may not be identical to + printing within the full module. +* `skip_regions` -- does not print regions of operations when the unit + attribute is specified. +""" +function print( + target=nothing::Union{Nothing,Value}; + name=nothing, + assume_verified=nothing, + use_local_scope=nothing, + skip_regions=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(target) && push!(_operands, target) + !isnothing(name) && push!(_attributes, namedattribute("name", name)) + !isnothing(assume_verified) && + push!(_attributes, namedattribute("assume_verified", assume_verified)) + !isnothing(use_local_scope) && + push!(_attributes, namedattribute("use_local_scope", use_local_scope)) + !isnothing(skip_regions) && + push!(_attributes, namedattribute("skip_regions", skip_regions)) + + return IR.create_operation( + "transform.print", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`replicate` + +Produces a new handle associated with a list of payload IR ops that is +computed by repeating the list of payload IR ops associated with the +operand handle as many times as the \"pattern\" handle has associated +operations. For example, if pattern is associated with [op1, op2] and the +operand handle is associated with [op3, op4, op5], the resulting handle +will be associated with [op3, op4, op5, op3, op4, op5]. + +This transformation is useful to \"align\" the sizes of payload IR lists +before a transformation that expects, e.g., identically-sized lists. For +example, a transformation may be parameterized by same notional per-target +size computed at runtime and supplied as another handle, the replication +allows this size to be computed only once and used for every target instead +of replicating the computation itself. + +Note that it is undesirable to pass a handle with duplicate operations to +an operation that consumes the handle. Handle consumption often indicates +that the associated payload IR ops are destroyed, so having the same op +listed more than once will lead to double-free. Single-operand +MergeHandlesOp may be used to deduplicate the associated list of payload IR +ops when necessary. Furthermore, a combination of ReplicateOp and +MergeHandlesOp can be used to construct arbitrary lists with repetitions. +""" +function replicate( + pattern::Value, handles::Vector{Value}; replicated::Vector{IR.Type}, location=Location() +) + _results = IR.Type[replicated...,] + _operands = Value[pattern, handles...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.replicate", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`select` + +The handle defined by this Transform op corresponds to all operations among +`target` that have the specified properties. Currently the following +properties are supported: + +- `op_name`: The op must have the specified name. + +The result payload ops are in the same relative order as the targeted ops. +This transform op reads the `target` handle and produces the `result` +handle. It reads the payload, but does not modify it. +""" +function select(target::Value; result::IR.Type, op_name, location=Location()) + _results = IR.Type[result,] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("op_name", op_name),] + + return IR.create_operation( + "transform.select", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`sequence` + +The transformations indicated by the sequence are applied in order of their +appearance. Each value produced by a transformation within the sequence +corresponds to a group of operations or values in the payload IR, or to a +group of parameters, depending on the type of the value. The behavior of the +operation when a nested transformation produces a silenceable error is +controlled by the `failure_propagation_mode` attribute. When set to +`propagate`, the failure of any nested transformation in the sequence +implies immediate failure of the entire sequence with a silenceable error, +and no further transformation is attempted. When set to `suppress`, +silenceable errors in nested operations are ignored and further +transformations are applied. Beware that even silenceable errors may leave +the payload IR in a state unsuitable for further transformations. It is the +responsibility of the caller to ensure the following transformations are +robust enough when errors are suppressed. Definite errors reported by nested +transformations abort the sequence regardless of the propagation mode. The +set of modes may be extended in the future, e.g., to collect silenceable +errors and report them after attempting all transformations in the sequence. + +The entry block of this operation has a single argument that maps to either +the operand if provided or the top-level container operation of the payload +IR, typically the root operation of the pass interpreting the transform +dialect. Operand omission is only allowed for sequences not contained in +another sequence. + +The type of the block argument must match the type of the operand. If the +sequence is a top-level transform (without an operand), it can be used for +matching operations if the specified type within the top-level container +payload IR (including the container op itself). E.g.: + +```mlir +transform.sequence failures(propagate) { +^bb1(%arg1: !transform.any_op): + // %arg1 is mapped to the top-level container of the payload IR, which is + // typically a module +} + +transform.sequence failures(propagate) { +^bb1(%arg1: !transform.op<\"func.func>\"): + // %arg1 is mapped to all \"func.func\" ops within and including the + // top-level container of the payload IR. Nested operations that have the + // specified op type are not included. +} +``` + +The body of the sequence terminates with an implicit or explicit +`transform.yield` op. The operands of the terminator are returned as the +results of the sequence op. +""" +function sequence( + root=nothing::Union{Nothing,Value}; + extra_bindings::Vector{Value}, + results::Vector{IR.Type}, + failure_propagation_mode, + body::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[extra_bindings...,] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute( + "failure_propagation_mode", failure_propagation_mode + ),] + !isnothing(root) && push!(_operands, root) + push!( + _attributes, operandsegmentsizes([isnothing(root) ? 0 : 1, length(extra_bindings)]) + ) + + return IR.create_operation( + "transform.sequence", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`split_handle` + +Splits `handle` into one or multiple handles, as specified by the number +of results of this operation. `handle` should be mapped to as many payload +ops as there are results. Otherwise, this transform will fail produces a +silenceable failure by default. Each result handle is mapped to exactly one +payload op. The order of the payload ops is preserved, i.e., the i-th +payload op is mapped to the i-th result handle. + +This operation is useful for ensuring a statically known number of +operations are tracked by the source `handle` and to extract them into +individual handles that can be further manipulated in isolation. + +If there are more payload ops than results, the remaining ops are mapped to +the result with index `overflow_result`. If no `overflow_result` is +specified, the transform produces a silenceable failure. + +If there are fewer payload ops than results, the transform produces a +silenceable failure if `fail_on_payload_too_small` is set to \"true\". +Otherwise, it succeeds and the remaining result handles are not mapped to +any op. It also succeeds if `handle` is empty and +`pass_through_empty_handle` is set to \"true\", regardless of +`fail_on_payload_too_small`. +""" +function split_handle( + handle::Value; + results::Vector{IR.Type}, + pass_through_empty_handle=nothing, + fail_on_payload_too_small=nothing, + overflow_result=nothing, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[handle,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(pass_through_empty_handle) && push!( + _attributes, + namedattribute("pass_through_empty_handle", pass_through_empty_handle), + ) + !isnothing(fail_on_payload_too_small) && push!( + _attributes, + namedattribute("fail_on_payload_too_small", fail_on_payload_too_small), + ) + !isnothing(overflow_result) && + push!(_attributes, namedattribute("overflow_result", overflow_result)) + + return IR.create_operation( + "transform.split_handle", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`verify` + +This transform verifies the targeted ops. If at least one op fails to +verify, the transform produces a definite failure. + +Note: This op was designed for debugging purposes and should be used like an +assertion. It is intentional that this op produces a definite failure and +not a silenceable one. Correctness of the program should not depend on this +op. + +This transform reads the target handle. +""" +function verify(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.verify", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +This terminator operation yields operation handles from regions of the +transform IR ops back to the containing op. It is not itself associated with +any transformation on the payload IR and is used for flow purposes only. +""" +function yield(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`debug_emit_param_as_remark` + +This operation emits a diagnostic remark containing the string form of the +attributes associated with the parameter provided as attribute. It takes +as optional arguments: + - an additional message text to prepend; + - a handle pointing to operations the location of which will be used to + emit the diagnostic; if multiple operations are associated, the + diagnostic is emitted for all of their respective locations. + +This operation always succeeds. +""" +function debug_emit_param_as_remark( + param::Value, anchor=nothing::Union{Nothing,Value}; message=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[param,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(anchor) && push!(_operands, anchor) + !isnothing(message) && push!(_attributes, namedattribute("message", message)) + + return IR.create_operation( + "transform.debug.emit_param_as_remark", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`debug_emit_remark_at` + +This operation emits a diagnostic remark with the given message at the +location of each payload object associated with the argument. The argument +may be an operation or a value handle. + +This operation always succeeds. +""" +function debug_emit_remark_at(at::Value; message, location=Location()) + _results = IR.Type[] + _operands = Value[at,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("message", message),] + + return IR.create_operation( + "transform.debug.emit_remark_at", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`loop_hoist_loop_invariant_subsets` + +This transform hoists loop-invariant subset ops out of the targeted +loop-like op. It looks for matching subset extraction/insertion op pairs and +hoists them. The loop body operates on a newly introduced region iter_arg. + +Subset ops are hoisted only from the targeted op. If subset ops should be +hoisted from an entire loop nest, this transformation must be applied to +each loop-like op of the loop nest, starting with the innermost loop and +ending with the outermost loop. + +# Example +``` +%r = scf.for ... iter_args(%t = %a) -> (tensor) { + %0 = tensor.extract_slice %t[0][5][1] : tensor to tensor<5xf32> + %1 = \"test.foo\"(%0) : (tensor<5xf32>) -> (tensor<5xf32>) + %2 = tensor.insert_slice %1 into %t[0][5][1] + : tensor<5xf32> into tensor + scf.yield %2 : tensor +} +``` +Is transformed to: +``` +%0 = tensor.extract_slice %a[0][5][1] : tensor to tensor<5xf32> +%new_loop:2 = scf.for ... iter_args(%t = %a, %h = %0) -> (tensor) { + %1 = \"test.foo\"(%h) : (tensor<5xf32>) -> (tensor<5xf32>) + scf.yield %t, %2 : tensor, tensor<5xf32> +} +%r = tensor.insert_slice %new_loop#1 into %new_loop#0 + : tensor<5xf32> into tensor +``` + +Subset ops are hoisted only if there are no conflicting subset ops. E.g., +if there were a second overlapping extraction in the above example, no ops +could be hoisted safely. + +This transform reads the target handle and modifies the payload. This +transform does not invalidate any handles, but loop-like ops are replaced +with new loop-like ops when a subset op is hoisted. The transform rewriter +updates all handles accordingly. +""" +function loop_hoist_loop_invariant_subsets(target::Value; location=Location()) + _results = IR.Type[] + _operands = Value[target,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.loop.hoist_loop_invariant_subsets", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`pdl_match` + +Find Payload IR ops nested within the Payload IR op associated with the +operand that match the PDL pattern identified by its name. The pattern is +expected to be defined in the closest surrounding `WithPDLPatternsOp`. + +Produces a Transform IR value associated with the list of Payload IR ops +that matched the pattern. The order of results in the list is that of the +Operation::walk, clients are advised not to rely on a specific order though. +If the operand is associated with multiple Payload IR ops, finds matching +ops nested within each of those and produces a single list containing all +of the matched ops. + +The transformation is considered successful regardless of whether some +Payload IR ops actually matched the pattern and only fails if the pattern +could not be looked up or compiled. +""" +function pdl_match(root::Value; matched::IR.Type, pattern_name, location=Location()) + _results = IR.Type[matched,] + _operands = Value[root,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("pattern_name", pattern_name),] + + return IR.create_operation( + "transform.pdl_match", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`with_pdl_patterns` + +This op contains a set of named PDL patterns that are available for the +Transform dialect operations to be used for pattern matching. For example, +PDLMatchOp can be used to produce a Transform IR value associated with all +Payload IR operations that match the pattern as follows: + +```mlir +transform.with_pdl_patterns { +^bb0(%arg0: !transform.any_op): + pdl.pattern @my_pattern : benefit(1) { + %0 = pdl.operation //... + // Regular PDL goes here. + pdl.rewrite %0 with \"transform.dialect\" + } + + sequence %arg0 failures(propagate) { + ^bb0(%arg1: !transform.any_op): + %1 = pdl_match @my_pattern in %arg1 + // Use %1 as handle + } +} +``` + +Note that the pattern is expected to finish with a `pdl.rewrite` terminator +that points to the custom rewriter named \"transform.dialect\". The rewriter +actually does nothing, but the transform application will keep track of the +operations that matched the pattern. + +This op is expected to contain `pdl.pattern` operations and exactly one +another Transform dialect operation that gets executed with all patterns +available. This op is a possible top-level Transform IR op, the argument of +its entry block corresponds to either the root op of the payload IR or the +ops associated with its operand when provided. +""" +function with_pdl_patterns( + root=nothing::Union{Nothing,Value}; body::Region, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[body,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(root) && push!(_operands, root) + + return IR.create_operation( + "transform.with_pdl_patterns", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`apply_patterns_vector_cast_away_vector_leading_one_dim` + +Collect a set of leading one dimension removal patterns. + +These patterns insert vector.shape_cast to remove leading one dimensions +to expose more canonical forms of read/write/insert/extract operations. +With them, there are more chances that we can cancel out extract-insert +pairs or forward write-read pairs. +""" +function apply_patterns_vector_cast_away_vector_leading_one_dim(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.cast_away_vector_leading_one_dim", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_fold_arith_extension` + +Collect a set of patterns that fold arithmetic extension on floating point +into vector contract for the backends with native support. +""" +function apply_patterns_vector_fold_arith_extension(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.fold_arith_extension", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_elementwise_to_vector` + +Collect a set of patterns that fold elementwise op on vectors to the vector +dialect. +""" +function apply_patterns_vector_elementwise_to_vector(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.elementwise_to_vector", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_interleave_to_shuffle` + +Indicates that 1D vector interleave operations should be rewritten as +vector shuffle operations. + +This is motivated by some current codegen backends not handling vector +interleave operations. +""" +function apply_patterns_vector_interleave_to_shuffle(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.interleave_to_shuffle", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_bitcast` + +Indicates that vector bitcast operations should be lowered to +finer-grained vector primitives. + +This is usally a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_lower_bitcast(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.lower_bitcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_broadcast` + +Indicates that vector broadcast operations should be lowered to +finer-grained vector primitives. + +This is usally a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_lower_broadcast(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.lower_broadcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_contraction` + +Indicates that vector contraction-like operations should be lowered to +finer-grained vector primitives. + +This is usually a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_lower_contraction(; + lowering_strategy=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lowering_strategy) && + push!(_attributes, namedattribute("lowering_strategy", lowering_strategy)) + + return IR.create_operation( + "transform.apply_patterns.vector.lower_contraction", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_create_mask` + +Indicates that vector create_mask-like operations should be lowered to +finer-grained vector primitives. +""" +function apply_patterns_vector_lower_create_mask(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.lower_create_mask", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_gather` + +Indicates that vector.gather operations should be lowered to +finer-grained vector primitives. +""" +function apply_patterns_vector_lower_gather(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.lower_gather", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_interleave` + +Indicates that vector interleave operations should be lowered to +finer-grained vector primitives. + +This is usally a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_lower_interleave(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.lower_interleave", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_masked_transfers` + +Apply opt-in patterns that lower vector.mask operations surrounding +side-effecting ops: + - MaskedTransferReadOpPattern + - MaskedTransferWriteOpPattern + - MaskedGatherOpPattern + +This is usually a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_lower_masked_transfers(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.lower_masked_transfers", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_masks` + +Indicates that vector.create_mask and vector.constant_mask operations +should be lowered to finer-grained vector primitives. + +This is usually a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_lower_masks(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.lower_masks", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_multi_reduction` + +Indicates that vector multi_reduction-like operations should be lowered to +finer-grained vector primitives. + +This is usually a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_lower_multi_reduction(; + lowering_strategy=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lowering_strategy) && + push!(_attributes, namedattribute("lowering_strategy", lowering_strategy)) + + return IR.create_operation( + "transform.apply_patterns.vector.lower_multi_reduction", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_outerproduct` + +Indicates that the vector outerproduct operations should be lowered to +finer-grained vector primitives. + +This is usually a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_lower_outerproduct(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.lower_outerproduct", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_scan` + +Indicates that vector.scan operations should be lowered to +finer-grained vector primitives. +""" +function apply_patterns_vector_lower_scan(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.lower_scan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_shape_cast` + +Indicates that vector shape_cast operations should be lowered to +finer-grained vector primitives. + +This is usually a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_lower_shape_cast(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.lower_shape_cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_transfer` + +Indicates that vector transfer operations should be lowered to finer-grained +vector primitives. + +This is usually a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_lower_transfer(; + max_transfer_rank=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(max_transfer_rank) && + push!(_attributes, namedattribute("max_transfer_rank", max_transfer_rank)) + + return IR.create_operation( + "transform.apply_patterns.vector.lower_transfer", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_lower_transpose` + +Indicates that vector transpose-like operations should be lowered to +finer-grained vector primitives. + +This is usually a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_lower_transpose(; + lowering_strategy=nothing, avx2_lowering_strategy=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(lowering_strategy) && + push!(_attributes, namedattribute("lowering_strategy", lowering_strategy)) + !isnothing(avx2_lowering_strategy) && + push!(_attributes, namedattribute("avx2_lowering_strategy", avx2_lowering_strategy)) + + return IR.create_operation( + "transform.apply_patterns.vector.lower_transpose", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_materialize_masks` + +Indicates that mask operations should be lowered to fine-grained arithemtic +operations. + +This is usually the last step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_materialize_masks(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.materialize_masks", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_rank_reducing_subview_patterns` + +Apply opt-in vector transfer permutation patterns that include: + - TransferReadDropUnitDimsPattern + - TransferWriteDropUnitDimsPattern + +These patterns have the effect of rewriting a vector.transfer with unit +dimensions into a rank-reduced version thanks to subview operations. +This is complemented by shape_cast folding patterns. +""" +function apply_patterns_vector_rank_reducing_subview_patterns(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.rank_reducing_subview_patterns", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_rewrite_narrow_types` + +Indicates that vector narrow rewrite operations should be applied. + +This is usually a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. + +Warning: these patterns currently only work for little endian targets. +""" +function apply_patterns_vector_rewrite_narrow_types(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.rewrite_narrow_types", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_split_transfer_full_partial` + +Indicates that vector transfer operations should be split to full and +partial parts. + +This is usually a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_split_transfer_full_partial(; + split_transfer_strategy=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(split_transfer_strategy) && push!( + _attributes, namedattribute("split_transfer_strategy", split_transfer_strategy) + ) + + return IR.create_operation( + "transform.apply_patterns.vector.split_transfer_full_partial", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_transfer_permutation_patterns` + +Apply opt-in vector transfer permutation patterns that include: + - TransferReadPermutationLowering + - TransferWritePermutationLowering + - TransferOpReduceRank + - TransferWriteNonPermutationLowering + +These patterns have the effect of rewriting a vector.transfer with an +arbitrary permutation_map to a vector.transfer with a permutation_map that +is a minor identity followed by a vector.transpose. + +In other words, this makes the vector.transfer contiguous on the most minor +dimensions and materializes the permutation_map as a vector.transpose. +""" +function apply_patterns_vector_transfer_permutation_patterns(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.transfer_permutation_patterns", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_transfer_to_scf` + +Indicates that vector transfer operations should be rewritten with scf.for +loops over finer-grained vector primitives. + +This is usually a late step that is run after bufferization as part of the +process of lowering to e.g. LLVM or NVVM. +""" +function apply_patterns_vector_transfer_to_scf(; + max_transfer_rank=nothing, full_unroll=nothing, location=Location() +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(max_transfer_rank) && + push!(_attributes, namedattribute("max_transfer_rank", max_transfer_rank)) + !isnothing(full_unroll) && + push!(_attributes, namedattribute("full_unroll", full_unroll)) + + return IR.create_operation( + "transform.apply_patterns.vector.transfer_to_scf", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_patterns_vector_reduction_to_contract` + +Apply opt-in patterns that convert reductions to contract: + - MultiReduceToContract + - CombineContractBroadcast + - CombineContractABTranspose + - CombineContractResultTranspose + - ReorderCastOpsOnBroadcast + - ReorderElementwiseOpsOnTranspose + +These patterns have the effect of rewriting a vector.multi_reduce into a +vector.contract. +""" +function apply_patterns_vector_reduction_to_contract(; location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "transform.apply_patterns.vector.reduction_to_contract", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`apply_conversion_patterns_vector_vector_to_llvm` + +Collects patterns that convert vector dialect ops to LLVM dialect ops. These +patterns require an \"LLVMTypeConverter\". + +The patterns can be customized as follows: +- `reassociate_fp_reductions`: Allows LLVM to reassociate floating-point + reductions for speed. +- `force_32bit_vector_indices`: Allows the compiler to assume that vector + indices fit in 32-bit if that yields faster code. +""" +function apply_conversion_patterns_vector_vector_to_llvm(; + reassociate_fp_reductions=nothing, + force_32bit_vector_indices=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(reassociate_fp_reductions) && push!( + _attributes, + namedattribute("reassociate_fp_reductions", reassociate_fp_reductions), + ) + !isnothing(force_32bit_vector_indices) && push!( + _attributes, + namedattribute("force_32bit_vector_indices", force_32bit_vector_indices), + ) + + return IR.create_operation( + "transform.apply_conversion_patterns.vector.vector_to_llvm", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # transform diff --git a/src/Dialects/19/UB.jl b/src/Dialects/19/UB.jl new file mode 100644 index 00000000..6884cd47 --- /dev/null +++ b/src/Dialects/19/UB.jl @@ -0,0 +1,45 @@ +module ub + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`poison` + +The `poison` operation materializes a compile-time poisoned constant value +to indicate deferred undefined behavior. +`value` attribute is needed to indicate an optional additional poison +semantics (e.g. partially poisoned vectors), default value indicates results +is fully poisoned. + +Examples: + +``` +// Short form +%0 = ub.poison : i32 +// Long form +%1 = ub.poison <#custom_poison_elements_attr> : vector<4xi64> +``` +""" +function poison(; result::IR.Type, value=nothing, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(value) && push!(_attributes, namedattribute("value", value)) + + return IR.create_operation( + "ub.poison", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # ub diff --git a/src/Dialects/19/Vector.jl b/src/Dialects/19/Vector.jl new file mode 100644 index 00000000..ebedc77f --- /dev/null +++ b/src/Dialects/19/Vector.jl @@ -0,0 +1,2868 @@ +module vector + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`vscale` + +The `vscale` op returns the scale of the scalable vectors, a positive +integer value that is constant at runtime but unknown at compile-time. +The scale of the vector indicates the multiplicity of the vectors and +vector operations. For example, a `vector<[4]xi32>` is equivalent to +`vscale` consecutive `vector<4xi32>`; and an operation on a +`vector<[4]xi32>` is equivalent to performing that operation `vscale` +times, once on each `<4xi32>` segment of the scalable vector. The `vscale` +op can be used to calculate the step in vector-length agnostic (VLA) loops. +Right now we only support one contiguous set of scalable dimensions, all of +them grouped and scaled with the value returned by \'vscale\'. +""" +function vscale(; res=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "vector.vscale", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`bitcast` + +The bitcast operation casts between vectors of the same rank, the minor 1-D +vector size is casted to a vector with a different element type but same +bitwidth. In case of 0-D vectors, the bitwidth of element types must be +equal. + +# Example + +```mlir +// Example casting to a smaller element type. +%1 = vector.bitcast %0 : vector<5x1x4x3xf32> to vector<5x1x4x6xi16> + +// Example casting to a bigger element type. +%3 = vector.bitcast %2 : vector<10x12x8xi8> to vector<10x12x2xi32> + +// Example casting to an element type of the same size. +%5 = vector.bitcast %4 : vector<5x1x4x3xf32> to vector<5x1x4x3xi32> + +// Example casting of 0-D vectors. +%7 = vector.bitcast %6 : vector to vector +``` +""" +function bitcast(source::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.bitcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`broadcast` + +Broadcasts the scalar or k-D vector value in the source operand +to a n-D result vector such that the broadcast makes sense, i.e., +the source operand is duplicated to match the given rank and sizes +in the result vector. The legality rules are: +* the source operand must have the same element type as the result type +* a k-D vector can be broadcast to + a n-D vector if + * k <= n, and + * the sizes in the trailing dimensions n-k < i <= n with j=i+k-n + match exactly as s_j = t_i or s_j = 1: + ``` + t_1 x .. t_n-k x t_n-k+1 x .. x t_i x .. x t_n + s_1 x .. x s_j x .. x s_k + + ``` +The source operand is duplicated over all the missing leading dimensions +and stretched over the trailing dimensions where the source has a non-equal +dimension of 1. These rules imply that any scalar broadcast (k=0) to any +shaped vector with the same element type is always legal. + +# Example + +```mlir +%0 = arith.constant 0.0 : f32 +%1 = vector.broadcast %0 : f32 to vector<16xf32> +%2 = vector.broadcast %1 : vector<16xf32> to vector<4x16xf32> +``` +""" +function broadcast(source::Value; vector::IR.Type, location=Location()) + _results = IR.Type[vector,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.broadcast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`compressstore` + +The compress store operation writes elements from a 1-D vector into memory +as defined by a base with indices and a 1-D mask vector. When the mask is +set, the corresponding element from the vector is written next to memory. +Otherwise, no action is taken for the element. Informally the semantics are: +``` +index = i +if (mask[0]) base[index++] = value[0] +if (mask[1]) base[index++] = value[1] +etc. +``` +Note that the index increment is done conditionally. + +If a mask bit is set and the corresponding index is out-of-bounds for the +given base, the behavior is undefined. If a mask bit is not set, no value +is stored regardless of the index, and the index is allowed to be +out-of-bounds. + +The compress store can be used directly where applicable, or can be used +during progressively lowering to bring other memory operations closer to +hardware ISA support for a compress. The semantics of the operation closely +correspond to those of the `llvm.masked.compressstore` +[intrinsic](https://llvm.org/docs/LangRef.html#llvm-masked-compressstore-intrinsics). + +Examples: + +```mlir +vector.compressstore %base[%i], %mask, %value + : memref, vector<8xi1>, vector<8xf32> + +vector.compressstore %base[%i, %j], %mask, %value + : memref, vector<16xi1>, vector<16xf32> +``` +""" +function compressstore( + base::Value, + indices::Vector{Value}, + mask::Value, + valueToStore::Value; + location=Location(), +) + _results = IR.Type[] + _operands = Value[base, indices..., mask, valueToStore] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.compressstore", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`constant_mask` + +Creates and returns a vector mask where elements of the result vector +are set to \'0\' or \'1\', based on whether the element indices are contained +within a hyper-rectangular region specified by the \'mask_dim_sizes\' +array attribute argument. Each element of the \'mask_dim_sizes\' array, +specifies an exclusive upper bound [0, mask-dim-size-element-value) +for a unique dimension in the vector result. The conjunction of the ranges +define a hyper-rectangular region within which elements values are set to 1 +(otherwise element values are set to 0). Each value of \'mask_dim_sizes\' must +be non-negative and not greater than the size of the corresponding vector +dimension (as opposed to vector.create_mask which allows this). Sizes that +correspond to scalable dimensions are implicitly multiplied by vscale, +though currently only zero (none set) or the size of the dim/vscale +(all set) are supported. + +# Example + +```mlir +// create a constant vector mask of size 4x3xi1 with elements in range +// 0 <= row <= 2 and 0 <= col <= 1 are set to 1 (others to 0). +%1 = vector.constant_mask [3, 2] : vector<4x3xi1> + +print %1 + columns + 0 1 2 + |------------ + 0 | 1 1 0 + rows 1 | 1 1 0 + 2 | 1 1 0 + 3 | 0 0 0 +``` +""" +function constant_mask(; result_0::IR.Type, mask_dim_sizes, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mask_dim_sizes", mask_dim_sizes),] + + return IR.create_operation( + "vector.constant_mask", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`contract` + +Computes the sum of products of vector elements along contracting +dimension pairs from 2 vectors of rank M and N respectively, adds this +intermediate result to the accumulator argument of rank K, and returns a +vector result of rank K (where K = num_lhs_free_dims + num_rhs_free_dims + +num_batch_dims (see dimension type descriptions below)). For K = 0 (no +free or batch dimensions), the accumulator and output are a scalar. + +If operands and the result have types of different bitwidths, operands are +promoted to have the same bitwidth as the result before performing the +contraction. For integer types, only signless integer types are supported, +and the promotion happens via sign extension. + +An iterator type attribute list must be specified, where each element of +the list represents an iterator with one of the following types: + +* \"reduction\": reduction dimensions are present in the lhs and rhs + arguments but not in the output (and accumulator + argument). These are the dimensions along which the vector + contraction op computes the sum of products, and + contracting dimension pair dimension sizes must match + between lhs/rhs. + +* \"parallel\": Batch dimensions are iterator type \"parallel\", and + are non-contracting dimensions present in the lhs, rhs and + output. The lhs/rhs co-iterate along the batch dimensions, + which should be expressed in their indexing maps. + + Free dimensions are iterator type \"parallel\", and are + non-contraction, non-batch dimensions accessed by either the + lhs or rhs (but not both). The lhs and rhs free dimensions + are unrelated to each other and do not co-iterate, which + should be expressed in their indexing maps. + +An indexing map attribute list must be specified with an entry for lhs, rhs +and acc arguments. An indexing map attribute specifies a mapping from each +iterator in the iterator type list, to each dimension of an N-D vector. + +An optional kind attribute may be used to specify the combining function +between the intermediate result and accumulator argument of rank K. This +attribute can take the values `add`/`mul`/`minsi`/`minui`/`maxsi`/`maxui` +/`and`/`or`/`xor` for integers, and `add`/`mul`/`minnumf`/`maxnumf` +/`minimumf`/`maximumf` for floats. The default is `add`. + +# Example + +```mlir +// Simple DOT product (K = 0). +#contraction_accesses = [ + affine_map<(i) -> (i)>, + affine_map<(i) -> (i)>, + affine_map<(i) -> ()> +] +#contraction_trait = { + indexing_maps = #contraction_accesses, + iterator_types = [\"reduction\"] +} +%3 = vector.contract #contraction_trait %0, %1, %2 + : vector<10xf32>, vector<10xf32> into f32 + +// 2D vector contraction with one contracting dimension (matmul, K = 2). +#contraction_accesses = [ + affine_map<(i, j, k) -> (i, k)>, + affine_map<(i, j, k) -> (k, j)>, + affine_map<(i, j, k) -> (i, j)> +] +#contraction_trait = { + indexing_maps = #contraction_accesses, + iterator_types = [\"parallel\", \"parallel\", \"reduction\"] +} + +%3 = vector.contract #contraction_trait %0, %1, %2 + : vector<4x3xf32>, vector<3x7xf32> into vector<4x7xf32> + +// 4D to 3D vector contraction with two contracting dimensions and +// one batch dimension (K = 3). +#contraction_accesses = [ + affine_map<(b0, f0, f1, c0, c1) -> (c0, b0, c1, f0)>, + affine_map<(b0, f0, f1, c0, c1) -> (b0, c1, c0, f1)>, + affine_map<(b0, f0, f1, c0, c1) -> (b0, f0, f1)> +] +#contraction_trait = { + indexing_maps = #contraction_accesses, + iterator_types = [\"parallel\", \"parallel\", \"parallel\", + \"reduction\", \"reduction\"] +} + +%4 = vector.contract #contraction_trait %0, %1, %2 + : vector<7x8x16x15xf32>, vector<8x16x7x5xf32> into vector<8x15x5xf32> + +// Vector contraction with mixed typed. lhs/rhs have different element +// types than accumulator/result. +%5 = vector.contract #contraction_trait %0, %1, %2 + : vector<10xf16>, vector<10xf16> into f32 + +// Contract with max (K = 0). +#contraction_accesses = [ + affine_map<(i) -> (i)>, + affine_map<(i) -> (i)>, + affine_map<(i) -> ()> +] +#contraction_trait = { + indexing_maps = #contraction_accesses, + iterator_types = [\"reduction\"], + kind = #vector.kind +} +%6 = vector.contract #contraction_trait %0, %1, %2 + : vector<10xf32>, vector<10xf32> into f32 +``` +""" +function contract( + lhs::Value, + rhs::Value, + acc::Value; + result_0::IR.Type, + indexing_maps, + iterator_types, + kind=nothing, + location=Location(), +) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs, acc] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("indexing_maps", indexing_maps), + namedattribute("iterator_types", iterator_types), + ] + !isnothing(kind) && push!(_attributes, namedattribute("kind", kind)) + + return IR.create_operation( + "vector.contract", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_mask` + +Creates and returns a vector mask where elements of the result vector +are set to \'0\' or \'1\', based on whether the element indices are contained +within a hyper-rectangular region specified by the operands. Specifically, +each operand specifies a range [0, operand-value) for a unique dimension in +the vector result. The conjunction of the operand ranges define a +hyper-rectangular region within which elements values are set to 1 +(otherwise element values are set to 0). If operand-value is negative, it is +treated as if it were zero, and if it is greater than the corresponding +dimension size, it is treated as if it were equal to the dimension size. + +# Example + +```mlir +// create a vector mask of size 4x3xi1 where elements in range +// 0 <= row <= 2 and 0 <= col <= 1 are set to 1 (others to 0). +%1 = vector.create_mask %c3, %c2 : vector<4x3xi1> + +print %1 + columns + 0 1 2 + |------------ + 0 | 1 1 0 + rows 1 | 1 1 0 + 2 | 1 1 0 + 3 | 0 0 0 +``` +""" +function create_mask(operands::Vector{Value}; result_0::IR.Type, location=Location()) + _results = IR.Type[result_0,] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.create_mask", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`deinterleave` + +The deinterleave operation constructs two vectors from a single input +vector. The first result vector contains the elements from even indexes +of the input, and the second contains elements from odd indexes. This is +the inverse of a `vector.interleave` operation. + +Each output\'s trailing dimension is half of the size of the input +vector\'s trailing dimension. This operation requires the input vector +to have a rank > 0 and an even number of elements in its trailing +dimension. + +The operation supports scalable vectors. + +# Example +```mlir +%0, %1 = vector.deinterleave %a + : vector<8xi8> -> vector<4xi8> +%2, %3 = vector.deinterleave %b + : vector<2x8xi8> -> vector<2x4xi8> +%4, %5 = vector.deinterleave %c + : vector<2x8x4xi8> -> vector<2x8x2xi8> +%6, %7 = vector.deinterleave %d + : vector<[8]xf32> -> vector<[4]xf32> +%8, %9 = vector.deinterleave %e + : vector<2x[6]xf64> -> vector<2x[3]xf64> +%10, %11 = vector.deinterleave %f + : vector<2x4x[6]xf64> -> vector<2x4x[3]xf64> +``` +""" +function deinterleave( + source::Value; + res1=nothing::Union{Nothing,IR.Type}, + res2=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res1) && push!(_results, res1) + !isnothing(res2) && push!(_results, res2) + + return IR.create_operation( + "vector.deinterleave", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`expandload` + +The expand load reads elements from memory into a 1-D vector as defined +by a base with indices and a 1-D mask vector. When the mask is set, the +next element is read from memory. Otherwise, the corresponding element +is taken from a 1-D pass-through vector. Informally the semantics are: +``` +index = i +result[0] := if mask[0] then base[index++] else pass_thru[0] +result[1] := if mask[1] then base[index++] else pass_thru[1] +etc. +``` +Note that the index increment is done conditionally. + +If a mask bit is set and the corresponding index is out-of-bounds for the +given base, the behavior is undefined. If a mask bit is not set, the value +comes from the pass-through vector regardless of the index, and the index is +allowed to be out-of-bounds. + +The expand load can be used directly where applicable, or can be used +during progressively lowering to bring other memory operations closer to +hardware ISA support for an expand. The semantics of the operation closely +correspond to those of the `llvm.masked.expandload` +[intrinsic](https://llvm.org/docs/LangRef.html#llvm-masked-expandload-intrinsics). + +Examples: + +```mlir +%0 = vector.expandload %base[%i], %mask, %pass_thru + : memref, vector<8xi1>, vector<8xf32> into vector<8xf32> + +%1 = vector.expandload %base[%i, %j], %mask, %pass_thru + : memref, vector<16xi1>, vector<16xf32> into vector<16xf32> +``` +""" +function expandload( + base::Value, + indices::Vector{Value}, + mask::Value, + pass_thru::Value; + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[base, indices..., mask, pass_thru] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.expandload", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`extractelement` + +Takes a 0-D or 1-D vector and a optional dynamic index position and +extracts the scalar at that position. + +Note that this instruction resembles vector.extract, but is restricted to +0-D and 1-D vectors and relaxed to dynamic indices. +If the vector is 0-D, the position must be std::nullopt. + + +It is meant to be closer to LLVM\'s version: +https://llvm.org/docs/LangRef.html#extractelement-instruction + +# Example + +```mlir +%c = arith.constant 15 : i32 +%1 = vector.extractelement %0[%c : i32]: vector<16xf32> +%2 = vector.extractelement %z[]: vector +``` +""" +function extractelement( + vector::Value, + position=nothing::Union{Nothing,Value}; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vector,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(position) && push!(_operands, position) + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "vector.extractelement", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`extract` + +Takes an n-D vector and a k-D position and extracts the (n-k)-D vector at +the proper position. Degenerates to an element type if n-k is zero. + +Dynamic indices must be greater or equal to zero and less than the size of +the corresponding dimension. The result is undefined if any index is +out-of-bounds. + +# Example + +```mlir +%1 = vector.extract %0[3]: vector<8x16xf32> from vector<4x8x16xf32> +%2 = vector.extract %0[2, 1, 3]: f32 from vector<4x8x16xf32> +%3 = vector.extract %1[]: vector from vector +%4 = vector.extract %0[%a, %b, %c]: f32 from vector<4x8x16xf32> +%5 = vector.extract %0[2, %b]: vector<16xf32> from vector<4x8x16xf32> +``` +""" +function extract( + vector::Value, + dynamic_position::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + static_position, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vector, dynamic_position...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("static_position", static_position),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "vector.extract", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`extract_strided_slice` + +Takes an n-D vector, k-D `offsets` integer array attribute, a k-sized +`sizes` integer array attribute, a k-sized `strides` integer array +attribute and extracts the n-D subvector at the proper offset. + +At the moment strides must contain only 1s. + +Returns an n-D vector where the first k-D dimensions match the `sizes` +attribute. The returned subvector contains the elements starting at offset +`offsets` and ending at `offsets + sizes`. + +# Example + +```mlir +%1 = vector.extract_strided_slice %0 + {offsets = [0, 2], sizes = [2, 4], strides = [1, 1]}: + vector<4x8x16xf32> to vector<2x4x16xf32> + +// TODO: Evolve to a range form syntax similar to: +%1 = vector.extract_strided_slice %0[0:2:1][2:4:1] + vector<4x8x16xf32> to vector<2x4x16xf32> +``` +""" +function extract_strided_slice( + vector::Value; result_0::IR.Type, offsets, sizes, strides, location=Location() +) + _results = IR.Type[result_0,] + _operands = Value[vector,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("offsets", offsets), + namedattribute("sizes", sizes), + namedattribute("strides", strides), + ] + + return IR.create_operation( + "vector.extract_strided_slice", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fma` + +Multiply-add expressions operate on n-D vectors and compute a fused +pointwise multiply-and-accumulate: `\$result = `\$lhs * \$rhs + \$acc`. +All operands and result have the same vector type. The semantics +of the operation correspond to those of the `llvm.fma` +[intrinsic](https://llvm.org/docs/LangRef.html#int-fma). In the +particular case of lowering to LLVM, this is guaranteed to lower +to the `llvm.fma.*` intrinsic. + +# Example + +```mlir +%3 = vector.fma %0, %1, %2: vector<8x16xf32> +``` +""" +function fma( + lhs::Value, + rhs::Value, + acc::Value; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[lhs, rhs, acc] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "vector.fma", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`flat_transpose` + +This is the counterpart of llvm.matrix.transpose in MLIR. It serves +the purposes of more progressive lowering and localized type conversion. +Higher levels typically lower matrix tranpositions into \'vector.transpose\' +operations. Subsequent rewriting rule progressively lower these operations +into \'vector.flat_transpose\' operations to bring the operations closer +to the hardware ISA. + +The `vector.flat_transpose` op treats the 1-D input `matrix` as +a 2-D matrix with rows and columns, and returns the +transposed matrix in flattened form in \'res\'. + +Also see: + +http://llvm.org/docs/LangRef.html#llvm-matrix-transpose-intrinsic + +# Example + +```mlir +%1 = vector.flat_transpose %0 {columns = 4 : i32, rows = 4 : i32} + : vector<16xf32> -> vector<16xf32> +``` +""" +function flat_transpose(matrix::Value; res::IR.Type, rows, columns, location=Location()) + _results = IR.Type[res,] + _operands = Value[matrix,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("rows", rows), namedattribute("columns", columns) + ] + + return IR.create_operation( + "vector.flat_transpose", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`from_elements` + +This operation defines a vector from one or multiple scalar elements. The +number of elements must match the number of elements in the result type. +All elements must have the same type, which must match the element type of +the result vector type. + +`elements` are a flattened version of the result vector in row-major order. + +# Example + +```mlir +// %f1 +%0 = vector.from_elements %f1 : vector +// [%f1, %f2] +%1 = vector.from_elements %f1, %f2 : vector<2xf32> +// [[%f1, %f2, %f3], [%f4, %f5, %f6]] +%2 = vector.from_elements %f1, %f2, %f3, %f4, %f5, %f6 : vector<2x3xf32> +// [[[%f1, %f2]], [[%f3, %f4]], [[%f5, %f6]]] +%3 = vector.from_elements %f1, %f2, %f3, %f4, %f5, %f6 : vector<3x1x2xf32> +``` +""" +function from_elements(elements::Vector{Value}; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[elements...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.from_elements", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`gather` + +The gather operation returns an n-D vector whose elements are either loaded +from memory or ranked tensor, or taken from a pass-through vector, depending +on the values of an n-D mask vector. +If a mask bit is set, the corresponding result element is defined by the base +with indices and the n-D index vector (each index is a 1-D offset on the base). +Otherwise, the corresponding element is taken from the n-D pass-through vector. +Informally the semantics are: +``` +result[0] := if mask[0] then base[index[0]] else pass_thru[0] +result[1] := if mask[1] then base[index[1]] else pass_thru[1] +etc. +``` + +If a mask bit is set and the corresponding index is out-of-bounds for the +given base, the behavior is undefined. If a mask bit is not set, the value +comes from the pass-through vector regardless of the index, and the index is +allowed to be out-of-bounds. + +The gather operation can be used directly where applicable, or can be used +during progressively lowering to bring other memory operations closer to +hardware ISA support for a gather. + +Examples: + +```mlir +%0 = vector.gather %base[%c0][%v], %mask, %pass_thru + : memref, vector<2x16xi32>, vector<2x16xi1>, vector<2x16xf32> into vector<2x16xf32> + +%1 = vector.gather %base[%i, %j][%v], %mask, %pass_thru + : memref<16x16xf32>, vector<16xi32>, vector<16xi1>, vector<16xf32> into vector<16xf32> +``` +""" +function gather( + base::Value, + indices::Vector{Value}, + index_vec::Value, + mask::Value, + pass_thru::Value; + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[base, indices..., index_vec, mask, pass_thru] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.gather", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`insertelement` + +Takes a scalar source, a 0-D or 1-D destination vector and a dynamic index +position and inserts the source into the destination at the proper position. + +Note that this instruction resembles vector.insert, but is restricted to 0-D +and 1-D vectors and relaxed to dynamic indices. + +It is meant to be closer to LLVM\'s version: +https://llvm.org/docs/LangRef.html#insertelement-instruction + +# Example + +```mlir +%c = arith.constant 15 : i32 +%f = arith.constant 0.0f : f32 +%1 = vector.insertelement %f, %0[%c : i32]: vector<16xf32> +%2 = vector.insertelement %f, %z[]: vector +``` +""" +function insertelement( + source::Value, + dest::Value, + position=nothing::Union{Nothing,Value}; + result=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source, dest] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(position) && push!(_operands, position) + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "vector.insertelement", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`insert` + +Takes an n-D source vector, an (n+k)-D destination vector and a k-D position +and inserts the n-D source into the (n+k)-D destination at the proper +position. Degenerates to a scalar or a 0-d vector source type when n = 0. + +Dynamic indices must be greater or equal to zero and less than the size of +the corresponding dimension. The result is undefined if any index is +out-of-bounds. + +# Example + +```mlir +%2 = vector.insert %0, %1[3] : vector<8x16xf32> into vector<4x8x16xf32> +%5 = vector.insert %3, %4[2, 1, 3] : f32 into vector<4x8x16xf32> +%8 = vector.insert %6, %7[] : f32 into vector +%11 = vector.insert %9, %10[%a, %b, %c] : vector into vector<4x8x16xf32> +%12 = vector.insert %4, %10[2, %b] : vector<16xf32> into vector<4x8x16xf32> +``` +""" +function insert( + source::Value, + dest::Value, + dynamic_position::Vector{Value}; + result=nothing::Union{Nothing,IR.Type}, + static_position, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source, dest, dynamic_position...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("static_position", static_position),] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "vector.insert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`insert_strided_slice` + +Takes a k-D source vector, an n-D destination vector (n >= k), n-sized +`offsets` integer array attribute, a k-sized `strides` integer array attribute +and inserts the k-D source vector as a strided subvector at the proper offset +into the n-D destination vector. + +At the moment strides must contain only 1s. + +Returns an n-D vector that is a copy of the n-D destination vector in which +the last k-D dimensions contain the k-D source vector elements strided at +the proper location as specified by the offsets. + +# Example + +```mlir +%2 = vector.insert_strided_slice %0, %1 + {offsets = [0, 0, 2], strides = [1, 1]}: + vector<2x4xf32> into vector<16x4x8xf32> +``` +""" +function insert_strided_slice( + source::Value, + dest::Value; + res=nothing::Union{Nothing,IR.Type}, + offsets, + strides, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source, dest] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("offsets", offsets), namedattribute("strides", strides) + ] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "vector.insert_strided_slice", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`interleave` + +The interleave operation constructs a new vector by interleaving the +elements from the trailing (or final) dimension of two input vectors, +returning a new vector where the trailing dimension is twice the size. + +Note that for the n-D case this differs from the interleaving possible with +`vector.shuffle`, which would only operate on the leading dimension. + +Another key difference is this operation supports scalable vectors, though +currently a general LLVM lowering is limited to the case where only the +trailing dimension is scalable. + +# Example +```mlir +%a = arith.constant dense<[0, 1]> : vector<2xi32> +%b = arith.constant dense<[2, 3]> : vector<2xi32> +// The value of `%0` is `[0, 2, 1, 3]`. +%0 = vector.interleave %a, %b : vector<2xi32> -> vector<4xi32> + +// Examples showing allowed input and result types. +%1 = vector.interleave %c, %d : vector -> vector<2xf16> +%2 = vector.interleave %e, %f : vector<6x3xf32> -> vector<6x6xf32> +%3 = vector.interleave %g, %h : vector<[4]xi32> -> vector<[8]xi32> +%4 = vector.interleave %i, %j : vector<2x4x[2]xf64> -> vector<2x4x[4]xf64> +``` +""" +function interleave( + lhs::Value, rhs::Value; result=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "vector.interleave", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`load` + +The \'vector.load\' operation reads an n-D slice of memory into an n-D +vector. It takes a \'base\' memref, an index for each memref dimension and a +result vector type as arguments. It returns a value of the result vector +type. The \'base\' memref and indices determine the start memory address from +which to read. Each index provides an offset for each memref dimension +based on the element type of the memref. The shape of the result vector +type determines the shape of the slice read from the start memory address. +The elements along each dimension of the slice are strided by the memref +strides. Only unit strides are allowed along the most minor memref +dimension. These constraints guarantee that elements read along the first +dimension of the slice are contiguous in memory. + +The memref element type can be a scalar or a vector type. If the memref +element type is a scalar, it should match the element type of the result +vector. If the memref element type is vector, it should match the result +vector type. + +# Example 0-D vector load on a scalar memref. +```mlir +%result = vector.load %base[%i, %j] : memref<100x100xf32>, vector +``` + +# Example 1-D vector load on a scalar memref. +```mlir +%result = vector.load %base[%i, %j] : memref<100x100xf32>, vector<8xf32> +``` + +# Example 1-D vector load on a vector memref. +```mlir +%result = vector.load %memref[%i, %j] : memref<200x100xvector<8xf32>>, vector<8xf32> +``` + +# Example 2-D vector load on a scalar memref. +```mlir +%result = vector.load %memref[%i, %j] : memref<200x100xf32>, vector<4x8xf32> +``` + +# Example 2-D vector load on a vector memref. +```mlir +%result = vector.load %memref[%i, %j] : memref<200x100xvector<4x8xf32>>, vector<4x8xf32> +``` + +Representation-wise, the \'vector.load\' operation permits out-of-bounds +reads. Support and implementation of out-of-bounds vector loads is +target-specific. No assumptions should be made on the value of elements +loaded out of bounds. Not all targets may support out-of-bounds vector +loads. + +# Example Potential out-of-bound vector load. +```mlir +%result = vector.load %memref[%index] : memref, vector<8xf32> +``` + +# Example Explicit out-of-bound vector load. +```mlir +%result = vector.load %memref[%c0] : memref<7xf32>, vector<8xf32> +``` +""" +function load( + base::Value, + indices::Vector{Value}; + result::IR.Type, + nontemporal=nothing, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[base, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(nontemporal) && + push!(_attributes, namedattribute("nontemporal", nontemporal)) + + return IR.create_operation( + "vector.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`mask` + +The `vector.mask` is a `MaskingOpInterface` operation that predicates the +execution of another operation. It takes an `i1` vector mask and an +optional passthru vector as arguments. + +A implicitly `vector.yield`-terminated region encloses the operation to be +masked. Values used within the region are captured from above. Only one +*maskable* operation can be masked with a `vector.mask` operation at a time. +An operation is *maskable* if it implements the `MaskableOpInterface`. The +terminator yields all results of the maskable operation to the result of +this operation. + +The vector mask argument holds a bit for each vector lane and determines +which vector lanes should execute the maskable operation and which ones +should not. The `vector.mask` operation returns the value produced by the +masked execution of the nested operation, if any. The masked-off lanes in +the result vector are taken from the corresponding lanes of the pass-thru +argument, if provided, or left unmodified, otherwise. + +The `vector.mask` operation does not prescribe how a maskable operation +should be masked or how a masked operation should be lowered. Masking +constraints and some semantic details are provided by each maskable +operation through the `MaskableOpInterface`. Lowering of masked operations +is implementation defined. For instance, scalarizing the masked operation +or executing the operation for the masked-off lanes are valid lowerings as +long as the execution of masked-off lanes does not change the observable +behavior of the program. + +Examples: + +``` + %0 = vector.mask %mask { vector.reduction , %a : vector<8xi32> into i32 } : vector<8xi1> -> i32 +``` + +``` + %0 = vector.mask %mask, %passthru { arith.divsi %a, %b : vector<8xi32> } : vector<8xi1> -> vector<8xi32> +``` + +``` + vector.mask %mask { vector.transfer_write %val, %t0[%idx] : vector<16xf32>, memref } : vector<16xi1> +``` + +``` + vector.mask %mask { vector.transfer_write %val, %t0[%idx] : vector<16xf32>, tensor } : vector<16xi1> -> tensor +``` +""" +function mask( + mask::Value, + passthru=nothing::Union{Nothing,Value}; + results::Vector{IR.Type}, + maskRegion::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[mask,] + _owned_regions = Region[maskRegion,] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(passthru) && push!(_operands, passthru) + + return IR.create_operation( + "vector.mask", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`maskedload` + +The masked load reads elements from memory into a 1-D vector as defined +by a base with indices and a 1-D mask vector. When the mask is set, the +element is read from memory. Otherwise, the corresponding element is taken +from a 1-D pass-through vector. Informally the semantics are: +``` +result[0] := if mask[0] then base[i + 0] else pass_thru[0] +result[1] := if mask[1] then base[i + 1] else pass_thru[1] +etc. +``` + +If a mask bit is set and the corresponding index is out-of-bounds for the +given base, the behavior is undefined. If a mask bit is not set, the value +comes from the pass-through vector regardless of the index, and the index is +allowed to be out-of-bounds. + +The masked load can be used directly where applicable, or can be used +during progressively lowering to bring other memory operations closer to +hardware ISA support for a masked load. The semantics of the operation +closely correspond to those of the `llvm.masked.load` +[intrinsic](https://llvm.org/docs/LangRef.html#llvm-masked-load-intrinsics). + +Examples: + +```mlir +%0 = vector.maskedload %base[%i], %mask, %pass_thru + : memref, vector<8xi1>, vector<8xf32> into vector<8xf32> + +%1 = vector.maskedload %base[%i, %j], %mask, %pass_thru + : memref, vector<16xi1>, vector<16xf32> into vector<16xf32> +``` +""" +function maskedload( + base::Value, + indices::Vector{Value}, + mask::Value, + pass_thru::Value; + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[base, indices..., mask, pass_thru] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.maskedload", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`maskedstore` + +The masked store operation writes elements from a 1-D vector into memory +as defined by a base with indices and a 1-D mask vector. When the mask is +set, the corresponding element from the vector is written to memory. Otherwise, +no action is taken for the element. Informally the semantics are: +``` +if (mask[0]) base[i+0] = value[0] +if (mask[1]) base[i+1] = value[1] +etc. +``` + +If a mask bit is set and the corresponding index is out-of-bounds for the +given base, the behavior is undefined. If a mask bit is not set, no value +is stored regardless of the index, and the index is allowed to be +out-of-bounds. + +The masked store can be used directly where applicable, or can be used +during progressively lowering to bring other memory operations closer to +hardware ISA support for a masked store. The semantics of the operation +closely correspond to those of the `llvm.masked.store` +[intrinsic](https://llvm.org/docs/LangRef.html#llvm-masked-store-intrinsics). + +Examples: + +```mlir +vector.maskedstore %base[%i], %mask, %value + : memref, vector<8xi1>, vector<8xf32> + +vector.maskedstore %base[%i, %j], %mask, %value + : memref, vector<16xi1>, vector<16xf32> +``` +""" +function maskedstore( + base::Value, + indices::Vector{Value}, + mask::Value, + valueToStore::Value; + location=Location(), +) + _results = IR.Type[] + _operands = Value[base, indices..., mask, valueToStore] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.maskedstore", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`matrix_multiply` + +This is the counterpart of llvm.matrix.multiply in MLIR. It serves the +purposes of more progressive lowering and localized type conversion. +Higher levels typically lower matrix multiplications into \'vector.contract\' +operations. Subsequent rewriting rule progressively lower these operations +into \'vector.matrix_multiply\' operations to bring the operations closer +to the hardware ISA. + +The ‘vector.matrix_multiply’ op treats `lhs` as matrix with rows +and columns, `rhs` as matrix with rows and + and multiplies them. The result matrix is returned embedded in +the result vector. + +Also see: + +http://llvm.org/docs/LangRef.html#llvm-matrix-multiply-intrinsic + +# Example + +```mlir +%C = vector.matrix_multiply %A, %B + { lhs_rows = 4: i32, lhs_columns = 16: i32 , rhs_columns = 3: i32 } : + (vector<64xf64>, vector<48xf64>) -> vector<12xf64> +``` +""" +function matrix_multiply( + lhs::Value, + rhs::Value; + res::IR.Type, + lhs_rows, + lhs_columns, + rhs_columns, + location=Location(), +) + _results = IR.Type[res,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("lhs_rows", lhs_rows), + namedattribute("lhs_columns", lhs_columns), + namedattribute("rhs_columns", rhs_columns), + ] + + return IR.create_operation( + "vector.matrix_multiply", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`multi_reduction` + +Reduces an n-D vector into an (n-k)-D vector (or a scalar when k == n) +using the given operation: `add`/`mul`/`minsi`/`minui`/`maxsi`/`maxui` +/`and`/`or`/`xor` for integers, and `add`/`mul`/`minnumf`/`maxnumf`/`minimumf` +/`maximumf` for floats. +Takes an initial accumulator operand. + +# Example + +```mlir +%1 = vector.multi_reduction , %0, %acc0 [1, 3] : + vector<4x8x16x32xf32> to vector<4x16xf32> +%2 = vector.multi_reduction , %1, %acc1 [0, 1] : + vector<4x16xf32> to f32 +``` +""" +function multi_reduction( + source::Value, + acc::Value; + dest=nothing::Union{Nothing,IR.Type}, + kind, + reduction_dims, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source, acc] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("kind", kind), namedattribute("reduction_dims", reduction_dims) + ] + !isnothing(dest) && push!(_results, dest) + + return IR.create_operation( + "vector.multi_reduction", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`outerproduct` + +Takes 2 1-D vectors and returns the 2-D vector containing the outer-product, +as illustrated below: +``` + outer | [c, d] + ------+------------ + [a, | [ [a*c, a*d], + b] | [b*c, b*d] ] +``` +This operation also accepts a 1-D vector lhs and a scalar rhs. In this +case a simple AXPY operation is performed, which returns a 1-D vector. +``` + [a, b] * c = [a*c, b*c] +``` + +An optional extra vector argument with the same shape as the output +vector may be specified in which case the operation returns the sum of +the outer-product and the extra vector. In this multiply-accumulate +scenario for floating-point arguments, the rounding mode is enforced +by guaranteeing that a fused-multiply add operation is emitted. When +lowered to the LLVMIR dialect, this form emits `llvm.intr.fma`, which +is guaranteed to lower to actual `fma` instructions on x86. + +An optional kind attribute may be specified to be: `add`/`mul`/`minsi` +/`minui`/`maxsi`/`maxui`/`and`/`or`/`xor` for integers, and `add`/`mul` +/`minnumf`/`maxnumf`/`minimumf`/`maximumf` for floats. The default is +`add`. + +# Example + +``` +%2 = vector.outerproduct %0, %1: vector<4xf32>, vector<8xf32> +return %2: vector<4x8xf32> + +%3 = vector.outerproduct %0, %1, %2: + vector<4xf32>, vector<8xf32>, vector<4x8xf32> +return %3: vector<4x8xf32> + +%4 = vector.outerproduct %0, %1, %2 {kind = #vector.kind}: + vector<4xf32>, vector<8xf32>, vector<4x8xf32> +return %3: vector<4x8xf32> + +%6 = vector.outerproduct %4, %5: vector<10xf32>, f32 +return %6: vector<10xf32> + +``` +""" +function outerproduct( + lhs::Value, + rhs::Value, + acc=nothing::Union{Nothing,Value}; + result_0::IR.Type, + kind=nothing, + location=Location(), +) + _results = IR.Type[result_0,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(acc) && push!(_operands, acc) + !isnothing(kind) && push!(_attributes, namedattribute("kind", kind)) + + return IR.create_operation( + "vector.outerproduct", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`print` + +Prints the source vector (or scalar) to stdout in a human-readable format +(for testing and debugging). No return value. + +# Example + +```mlir +%v = arith.constant dense<0.0> : vector<4xf32> +vector.print %v : vector<4xf32> +``` + +When lowered to LLVM, the vector print is decomposed into elementary +printing method calls that at runtime will yield: + +``` +( 0.0, 0.0, 0.0, 0.0 ) +``` + +This is printed to stdout via a small runtime support library, which only +needs to provide a few printing methods (single value for all data +types, opening/closing bracket, comma, newline). + +By default `vector.print` adds a newline after the vector, but this can be +controlled by the `punctuation` attribute. For example, to print a comma +after instead do: + +```mlir +vector.print %v : vector<4xf32> punctuation +``` + +Note that it is possible to use the punctuation attribute alone. The +following will print a single newline: + +```mlir +vector.print punctuation +``` + +Additionally, to aid with debugging and testing `vector.print` can also +print constant strings: + +```mlir +vector.print str \"Hello, World!\" +``` +""" +function print( + source=nothing::Union{Nothing,Value}; + punctuation=nothing, + stringLiteral=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(source) && push!(_operands, source) + !isnothing(punctuation) && + push!(_attributes, namedattribute("punctuation", punctuation)) + !isnothing(stringLiteral) && + push!(_attributes, namedattribute("stringLiteral", stringLiteral)) + + return IR.create_operation( + "vector.print", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reduction` + +Reduces an 1-D vector \"horizontally\" into a scalar using the given +operation: `add`/`mul`/`minsi`/`minui`/`maxsi`/`maxui`/`and`/`or`/`xor` for +integers, and `add`/`mul`/`minnumf`/`maxnumf`/`minimumf`/`maximumf` for +floats. Reductions also allow an optional fused accumulator. + +Note that these operations are restricted to 1-D vectors to remain +close to the corresponding LLVM intrinsics: + +http://llvm.org/docs/LangRef.html#vector-reduction-intrinsics + +# Example + +```mlir +%1 = vector.reduction , %0 : vector<16xf32> into f32 + +%3 = vector.reduction , %2 : vector<4xi32> into i32 + +%4 = vector.reduction , %0, %1 : vector<16xf32> into f32 +``` +""" +function reduction( + vector::Value, + acc=nothing::Union{Nothing,Value}; + dest::IR.Type, + kind, + fastmath=nothing, + location=Location(), +) + _results = IR.Type[dest,] + _operands = Value[vector,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("kind", kind),] + !isnothing(acc) && push!(_operands, acc) + !isnothing(fastmath) && push!(_attributes, namedattribute("fastmath", fastmath)) + + return IR.create_operation( + "vector.reduction", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`reshape` + +Reshapes its vector operand from \'input_shape\' to \'output_shape\' maintaining +fixed vector dimension \'fixed_vector_sizes\' on the innermost vector +dimensions. + +The parameters \'input_shape\' and \'output_shape\' represent valid data shapes +across fixed vector shapes. For example, if a vector has a valid data +shape [6] with fixed vector size [8], then the valid data elements are +assumed to be stored at the beginning of the vector with the remaining +vector elements undefined. + +In the examples below, valid data elements are represented by an alphabetic +character, and undefined data elements are represented by \'-\'. + +Example + + vector<1x8xf32> with valid data shape [6], fixed vector sizes [8] + + input: [a, b, c, d, e, f] + + layout map: (d0) -> (d0 floordiv 8, d0 mod 8) + + vector layout: [a, b, c, d, e, f, -, -] + +Example + + vector<2x8xf32> with valid data shape [10], fixed vector sizes [8] + + input: [a, b, c, d, e, f, g, h, i, j] + + layout map: (d0) -> (d0 floordiv 8, d0 mod 8) + + vector layout: [[a, b, c, d, e, f, g, h], + [i, j, -, -, -, -, -, -]] + +Example + + vector<2x2x2x3xf32> with valid data shape [3, 5], fixed vector sizes + [2, 3] + + input: [[a, b, c, d, e], + [f, g, h, i, j], + [k, l, m, n, o]] + + layout map: (d0, d1) -> (d0 floordiv 3, d1 floordiv 5, + d0 mod 3, d1 mod 5) + + vector layout: [[[[a, b, c], + [f, g, h]] + [[d, e, -], + [i, j, -]]], + [[[k, l, m], + [-, -, -]] + [[n, o, -], + [-, -, -]]]] + +Example + + %1 = vector.reshape %0, [%c3, %c6], [%c2, %c9], [4] + : vector<3x2x4xf32> to vector<2x3x4xf32> + + input: [[a, b, c, d, e, f], + [g, h, i, j, k, l], + [m, n, o, p, q, r]] + + layout map: (d0, d1) -> (d0, d1 floordiv 4, d1 mod 4) + + + Input vector: [[[a, b, c, d], + [e, f, -, -]], + [[g, h, i, j], + [k, l, -, -]], + [[m, n, o, p], + [q, r, -, -]]] + + Output vector: [[[a, b, c, d], + [e, f, g, h], + [i, -, -, -]], + [[j, k, l, m], + [n, o, p, q], + [r, -, -, -]]] +""" +function reshape( + vector::Value, + input_shape::Vector{Value}, + output_shape::Vector{Value}; + result::IR.Type, + fixed_vector_sizes, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[vector, input_shape..., output_shape...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("fixed_vector_sizes", fixed_vector_sizes),] + push!(_attributes, operandsegmentsizes([1, length(input_shape), length(output_shape)])) + + return IR.create_operation( + "vector.reshape", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`scalable_extract` + +Takes rank-1 source vector and a position `pos` within the source +vector, and extracts a subvector starting from that position. + +The extraction position must be a multiple of the minimum size of the result +vector. For the operation to be well defined, the destination vector must +fit within the source vector from the specified position. Since the source +vector is scalable and its runtime length is unknown, the validity of the +operation can\'t be verified nor guaranteed at compile time. + +# Example + +```mlir +%1 = vector.scalable.extract %0[8] : vector<4xf32> from vector<[8]xf32> +%3 = vector.scalable.extract %2[0] : vector<[4]xf32> from vector<[8]xf32> +``` + +Invalid example: +```mlir +%1 = vector.scalable.extract %0[5] : vector<4xf32> from vector<[16]xf32> +``` +""" +function scalable_extract(source::Value; res::IR.Type, pos, location=Location()) + _results = IR.Type[res,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("pos", pos),] + + return IR.create_operation( + "vector.scalable.extract", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`scalable_insert` + +This operations takes a rank-1 fixed-length or scalable subvector and +inserts it within the destination scalable vector starting from the +position specificed by `pos`. If the source vector is scalable, the +insertion position will be scaled by the runtime scaling factor of the +source subvector. + +The insertion position must be a multiple of the minimum size of the source +vector. For the operation to be well defined, the source vector must fit in +the destination vector from the specified position. Since the destination +vector is scalable and its runtime length is unknown, the validity of the +operation can\'t be verified nor guaranteed at compile time. + +# Example + +```mlir +%2 = vector.scalable.insert %0, %1[8] : vector<4xf32> into vector<[16]xf32> +%5 = vector.scalable.insert %3, %4[0] : vector<8xf32> into vector<[4]xf32> +%8 = vector.scalable.insert %6, %7[0] : vector<[4]xf32> into vector<[8]xf32> +``` + +Invalid example: +```mlir +%2 = vector.scalable.insert %0, %1[5] : vector<4xf32> into vector<[16]xf32> +``` +""" +function scalable_insert( + source::Value, + dest::Value; + res=nothing::Union{Nothing,IR.Type}, + pos, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source, dest] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("pos", pos),] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "vector.scalable.insert", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`scan` + +Performs an inclusive/exclusive scan on an n-D vector along a single +dimension returning an n-D result vector using the given +operation (`add`/`mul`/`minsi`/`minui`/`maxsi`/`maxui`/`and`/`or`/`xor` for +integers, and `add`/`mul`/`minnumf`/`maxnumf`/`minimumf`/`maximumf` for +floats), and a specified value for the initial value. The operator returns +the result of scan as well as the result of the last reduction in the scan. + +# Example + +```mlir +%1:2 = vector.scan , %0, %acc {inclusive = false, reduction_dim = 1 : i64} : + vector<4x8x16x32xf32>, vector<4x16x32xf32> +``` +""" +function scan( + source::Value, + initial_value::Value; + dest=nothing::Union{Nothing,IR.Type}, + accumulated_value=nothing::Union{Nothing,IR.Type}, + kind, + reduction_dim, + inclusive, + location=Location(), +) + _results = IR.Type[] + _operands = Value[source, initial_value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("kind", kind), + namedattribute("reduction_dim", reduction_dim), + namedattribute("inclusive", inclusive), + ] + !isnothing(dest) && push!(_results, dest) + !isnothing(accumulated_value) && push!(_results, accumulated_value) + + return IR.create_operation( + "vector.scan", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`scatter` + +The scatter operation stores elements from a 1-D vector into memory as +defined by a base with indices and an additional 1-D index vector, but +only if the corresponding bit in a 1-D mask vector is set. Otherwise, no +action is taken for that element. Informally the semantics are: +``` +if (mask[0]) base[index[0]] = value[0] +if (mask[1]) base[index[1]] = value[1] +etc. +``` + +If a mask bit is set and the corresponding index is out-of-bounds for the +given base, the behavior is undefined. If a mask bit is not set, no value +is stored regardless of the index, and the index is allowed to be +out-of-bounds. + +If the index vector contains two or more duplicate indices, the behavior is +undefined. Underlying implementation may enforce strict sequential +semantics. +TODO: always enforce strict sequential semantics? + +The scatter operation can be used directly where applicable, or can be used +during progressively lowering to bring other memory operations closer to +hardware ISA support for a scatter. The semantics of the operation closely +correspond to those of the `llvm.masked.scatter` +[intrinsic](https://llvm.org/docs/LangRef.html#llvm-masked-scatter-intrinsics). + +Examples: + +```mlir +vector.scatter %base[%c0][%v], %mask, %value + : memref, vector<16xi32>, vector<16xi1>, vector<16xf32> + +vector.scatter %base[%i, %j][%v], %mask, %value + : memref<16x16xf32>, vector<16xi32>, vector<16xi1>, vector<16xf32> +``` +""" +function scatter( + base::Value, + indices::Vector{Value}, + index_vec::Value, + mask::Value, + valueToStore::Value; + location=Location(), +) + _results = IR.Type[] + _operands = Value[base, indices..., index_vec, mask, valueToStore] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.scatter", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`shape_cast` + +The shape_cast operation casts between an n-D source vector shape and +a k-D result vector shape (the element type remains the same). + +If reducing rank (n > k), result dimension sizes must be a product +of contiguous source dimension sizes. +If expanding rank (n < k), source dimensions must factor into a +contiguous sequence of destination dimension sizes. +Each source dim is expanded (or contiguous sequence of source dims combined) +in source dimension list order (i.e. 0 <= i < n), to produce a contiguous +sequence of result dims (or a single result dim), in result dimension list +order (i.e. 0 <= j < k). The product of all source dimension sizes and all +result dimension sizes must match. + +It is currently assumed that this operation does not require moving data, +and that it will be folded away before lowering vector operations. + +There is an exception to the folding expectation when targeting +llvm.intr.matrix operations. We need a type conversion back and forth from a +2-D MLIR vector to a 1-D flattened LLVM vector.shape_cast lowering to LLVM +is supported in that particular case, for now. + +# Example + +```mlir +// Example casting to a lower vector rank. +%1 = vector.shape_cast %0 : vector<5x1x4x3xf32> to vector<20x3xf32> + +// Example casting to a higher vector rank. +%3 = vector.shape_cast %2 : vector<10x12x8xf32> to vector<5x2x3x4x8xf32> + +``` +""" +function shape_cast(source::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[source,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.shape_cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`shuffle` + +The shuffle operation constructs a permutation (or duplication) of elements +from two input vectors, returning a vector with the same element type as +the input and a length that is the same as the shuffle mask. The two input +vectors must have the same element type, same rank , and trailing dimension +sizes and shuffles their values in the +leading dimension (which may differ in size) according to the given mask. +The legality rules are: +* the two operands must have the same element type as the result + - Either, the two operands and the result must have the same + rank and trailing dimension sizes, viz. given two k-D operands + v1 : and + v2 : + we have s_i = t_i for all 1 < i <= k + - Or, the two operands must be 0-D vectors and the result is a 1-D vector. +* the mask length equals the leading dimension size of the result +* numbering the input vector indices left to right across the operands, all + mask values must be within range, viz. given two k-D operands v1 and v2 + above, all mask values are in the range [0,s_1+t_1) + +Note, scalable vectors are not supported. + +# Example + +```mlir +%0 = vector.shuffle %a, %b[0, 3] + : vector<2xf32>, vector<2xf32> ; yields vector<2xf32> +%1 = vector.shuffle %c, %b[0, 1, 2] + : vector<2x16xf32>, vector<1x16xf32> ; yields vector<3x16xf32> +%2 = vector.shuffle %a, %b[3, 2, 1, 0] + : vector<2xf32>, vector<2xf32> ; yields vector<4xf32> +%3 = vector.shuffle %a, %b[0, 1] + : vector, vector ; yields vector<2xf32> +``` +""" +function shuffle( + v1::Value, v2::Value; vector=nothing::Union{Nothing,IR.Type}, mask, location=Location() +) + _results = IR.Type[] + _operands = Value[v1, v2] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("mask", mask),] + !isnothing(vector) && push!(_results, vector) + + return IR.create_operation( + "vector.shuffle", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`splat` + +Broadcast the operand to all elements of the result vector. The operand is +required to be of integer/index/float type. + +# Example + +```mlir +%s = arith.constant 10.1 : f32 +%t = vector.splat %s : vector<8x16xi32> +``` +""" +function splat(input::Value; aggregate::IR.Type, location=Location()) + _results = IR.Type[aggregate,] + _operands = Value[input,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.splat", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`step` + +A `step` operation produces an index vector, i.e. a 1-D vector of values of +index type that represents a linear sequence from 0 to N-1, where N is the +number of elements in the `result` vector. + +Supports fixed-width and scalable vectors. + +Examples: + +```mlir +%0 = vector.step : vector<4xindex> ; [0, 1, 2, 3] +%1 = vector.step : vector<[4]xindex> ; [0, 1, .., ] +``` +""" +function step(; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.step", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`store` + +The \'vector.store\' operation writes an n-D vector to an n-D slice of memory. +It takes the vector value to be stored, a \'base\' memref and an index for +each memref dimension. The \'base\' memref and indices determine the start +memory address from which to write. Each index provides an offset for each +memref dimension based on the element type of the memref. The shape of the +vector value to store determines the shape of the slice written from the +start memory address. The elements along each dimension of the slice are +strided by the memref strides. Only unit strides are allowed along the most +minor memref dimension. These constraints guarantee that elements written +along the first dimension of the slice are contiguous in memory. + +The memref element type can be a scalar or a vector type. If the memref +element type is a scalar, it should match the element type of the value +to store. If the memref element type is vector, it should match the type +of the value to store. + +# Example 0-D vector store on a scalar memref. +```mlir +vector.store %valueToStore, %memref[%i, %j] : memref<200x100xf32>, vector +``` + +# Example 1-D vector store on a scalar memref. +```mlir +vector.store %valueToStore, %memref[%i, %j] : memref<200x100xf32>, vector<8xf32> +``` + +# Example 1-D vector store on a vector memref. +```mlir +vector.store %valueToStore, %memref[%i, %j] : memref<200x100xvector<8xf32>>, vector<8xf32> +``` + +# Example 2-D vector store on a scalar memref. +```mlir +vector.store %valueToStore, %memref[%i, %j] : memref<200x100xf32>, vector<4x8xf32> +``` + +# Example 2-D vector store on a vector memref. +```mlir +vector.store %valueToStore, %memref[%i, %j] : memref<200x100xvector<4x8xf32>>, vector<4x8xf32> +``` + +Representation-wise, the \'vector.store\' operation permits out-of-bounds +writes. Support and implementation of out-of-bounds vector stores are +target-specific. No assumptions should be made on the memory written out of +bounds. Not all targets may support out-of-bounds vector stores. + +# Example Potential out-of-bounds vector store. +```mlir +vector.store %valueToStore, %memref[%index] : memref, vector<8xf32> +``` + +# Example Explicit out-of-bounds vector store. +```mlir +vector.store %valueToStore, %memref[%c0] : memref<7xf32>, vector<8xf32> +``` +""" +function store( + valueToStore::Value, + base::Value, + indices::Vector{Value}; + nontemporal=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[valueToStore, base, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(nontemporal) && + push!(_attributes, namedattribute("nontemporal", nontemporal)) + + return IR.create_operation( + "vector.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`transfer_read` + +The `vector.transfer_read` op performs a read from a slice within a +[MemRef](../LangRef.md#memref-type) or a Ranked +[Tensor](../LangRef.md#tensor-type) supplied as its first operand +into a [vector](../LangRef.md#vector-type) of the same base elemental type. + +A memref/tensor operand with vector element type, must have its vector +element type match a suffix (shape and element type) of the vector (e.g. +memref<3x2x6x4x3xf32>, vector<1x1x4x3xf32>). + +The slice is further defined by a full-rank index within the MemRef/Tensor, +supplied as the operands `[1 .. 1 + rank(memref/tensor))` that defines the +starting point of the transfer (e.g. `%A[%i0, %i1, %i2]`). + +The permutation_map [attribute](../LangRef.md#attributes) is an +[affine-map](Affine.md#affine-maps) which specifies the transposition on the +slice to match the vector shape. The permutation map may be implicit and +omitted from parsing and printing if it is the canonical minor identity map +(i.e. if it does not permute or broadcast any dimension). + +The size of the slice is specified by the size of the vector, given as the +return type. + +An SSA value `padding` of the same elemental type as the MemRef/Tensor is +provided to specify a fallback value in the case of out-of-bounds accesses +and/or masking. + +An optional SSA value `mask` may be specified to mask out elements read from +the MemRef/Tensor. The `mask` type is an `i1` vector with a shape that +matches how elements are read from the MemRef/Tensor, *before* any +permutation or broadcasting. Elements whose corresponding mask element is +`0` are masked out and replaced with `padding`. + +For every vector dimension, the boolean array attribute `in_bounds` +specifies if the transfer is guaranteed to be within the source bounds. If +set to \"false\", accesses (including the starting point) may run +out-of-bounds along the respective vector dimension as the index increases. +Non-vector and broadcast dimensions *must* always be in-bounds. The +`in_bounds` array length has to be equal to the vector rank. This attribute +has a default value: `false` (i.e. \"out-of-bounds\"). When skipped in the +textual IR, the default value is assumed. Similarly, the OP printer will +omit this attribute when all dimensions are out-of-bounds (i.e. the default +value is used). + +A `vector.transfer_read` can be lowered to a simple load if all dimensions +are specified to be within bounds and no `mask` was specified. + +This operation is called \'read\' by opposition to \'load\' because the +super-vector granularity is generally not representable with a single +hardware register. A `vector.transfer_read` is thus a mid-level abstraction +that supports super-vectorization with non-effecting padding for full-tile +only operations. + +More precisely, let\'s dive deeper into the permutation_map for the following +MLIR: + +```mlir +vector.transfer_read %A[%expr1, %expr2, %expr3, %expr4] + { permutation_map : (d0,d1,d2,d3) -> (d2,0,d0) } : + memref, vector<3x4x5xf32> +``` + +This operation always reads a slice starting at `%A[%expr1, %expr2, %expr3, +%expr4]`. The size of the slice can be inferred from the resulting vector +shape and walking back through the permutation map: 3 along d2 and 5 along +d0, so the slice is: `%A[%expr1 : %expr1 + 5, %expr2, %expr3:%expr3 + 3, %expr4]` + +That slice needs to be read into a `vector<3x4x5xf32>`. Since the +permutation map is not full rank, there must be a broadcast along vector +dimension `1`. + +A notional lowering of vector.transfer_read could generate code resembling: + +```mlir +// %expr1, %expr2, %expr3, %expr4 defined before this point +// alloc a temporary buffer for performing the \"gather\" of the slice. +%tmp = memref.alloc() : memref> +for %i = 0 to 3 { + affine.for %j = 0 to 4 { + affine.for %k = 0 to 5 { + // Note that this load does not involve %j. + %a = load %A[%expr1 + %k, %expr2, %expr3 + %i, %expr4] : memref + // Update the temporary gathered slice with the individual element + %slice = memref.load %tmp : memref> -> vector<3x4x5xf32> + %updated = vector.insert %a, %slice[%i, %j, %k] : f32 into vector<3x4x5xf32> + memref.store %updated, %tmp : memref> +}}} +// At this point we gathered the elements from the original +// memref into the desired vector layout, stored in the `%tmp` allocation. +%vec = memref.load %tmp : memref> -> vector<3x4x5xf32> +``` + +On a GPU one could then map `i`, `j`, `k` to blocks and threads. Notice that +the temporary storage footprint could conceptually be only `3 * 5` values but +`3 * 4 * 5` values are actually transferred between `%A` and `%tmp`. + +Alternatively, if a notional vector broadcast operation were available, we +could avoid the loop on `%j` and the lowered code would resemble: + +```mlir +// %expr1, %expr2, %expr3, %expr4 defined before this point +%tmp = memref.alloc() : memref> +for %i = 0 to 3 { + affine.for %k = 0 to 5 { + %a = load %A[%expr1 + %k, %expr2, %expr3 + %i, %expr4] : memref + %slice = memref.load %tmp : memref> -> vector<3x4x5xf32> + // Here we only store to the first element in dimension one + %updated = vector.insert %a, %slice[%i, 0, %k] : f32 into vector<3x4x5xf32> + memref.store %updated, %tmp : memref> +}} +// At this point we gathered the elements from the original +// memref into the desired vector layout, stored in the `%tmp` allocation. +// However we haven\'t replicated them alongside the first dimension, we need +// to broadcast now. +%partialVec = load %tmp : memref> -> vector<3x4x5xf32> +%vec = broadcast %tmpvec, 1 : vector<3x4x5xf32> +``` + +where `broadcast` broadcasts from element 0 to all others along the +specified dimension. This time, the number of loaded element is `3 * 5` +values. +An additional `1` broadcast is required. On a GPU this broadcast could be +implemented using a warp-shuffle if loop `j` were mapped to `threadIdx.x`. + +Syntax +``` +operation ::= ssa-id `=` `vector.transfer_read` ssa-use-list + `{` attribute-entry `} :` memref-type `,` vector-type +``` + +# Example + +```mlir +// Read the slice `%A[%i0, %i1:%i1+256, %i2:%i2+32]` into vector<32x256xf32> +// and pad with %f0 to handle the boundary case: +%f0 = arith.constant 0.0f : f32 +affine.for %i0 = 0 to %0 { + affine.for %i1 = 0 to %1 step 256 { + affine.for %i2 = 0 to %2 step 32 { + %v = vector.transfer_read %A[%i0, %i1, %i2], (%f0) + {permutation_map: (d0, d1, d2) -> (d2, d1)} : + memref, vector<32x256xf32> +}}} + +// or equivalently (rewrite with vector.transpose) +%f0 = arith.constant 0.0f : f32 +affine.for %i0 = 0 to %0 { + affine.for %i1 = 0 to %1 step 256 { + affine.for %i2 = 0 to %2 step 32 { + %v0 = vector.transfer_read %A[%i0, %i1, %i2], (%f0) + {permutation_map: (d0, d1, d2) -> (d1, d2)} : + memref, vector<256x32xf32> + %v = vector.transpose %v0, [1, 0] : + vector<256x32xf32> to vector<32x256f32> +}}} + +// Read the slice `%A[%i0, %i1]` (i.e. the element `%A[%i0, %i1]`) into +// vector<128xf32>. The underlying implementation will require a 1-D vector +// broadcast: +affine.for %i0 = 0 to %0 { + affine.for %i1 = 0 to %1 { + %3 = vector.transfer_read %A[%i0, %i1] + {permutation_map: (d0, d1) -> (0)} : + memref, vector<128xf32> + } +} + +// Read from a memref with vector element type. +%4 = vector.transfer_read %arg1[%c3, %c3], %vf0 + {permutation_map = (d0, d1)->(d0, d1)} + : memref>, vector<1x1x4x3xf32> + +// Read from a tensor with vector element type. +%4 = vector.transfer_read %arg1[%c3, %c3], %vf0 + {permutation_map = (d0, d1)->(d0, d1)} + : tensor>, vector<1x1x4x3xf32> + +// Special encoding for 0-d transfer with 0-d tensor/memref, vector shape +// {1} and permutation_map () -> (0). +%0 = vector.transfer_read %arg0[], %f0 {permutation_map = affine_map<()->(0)>} : + tensor, vector<1xf32> +``` +""" +function transfer_read( + source::Value, + indices::Vector{Value}, + padding::Value, + mask=nothing::Union{Nothing,Value}; + vector::IR.Type, + permutation_map, + in_bounds, + location=Location(), +) + _results = IR.Type[vector,] + _operands = Value[source, indices..., padding] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("permutation_map", permutation_map), + namedattribute("in_bounds", in_bounds), + ] + !isnothing(mask) && push!(_operands, mask) + push!( + _attributes, operandsegmentsizes([1, length(indices), 1, isnothing(mask) ? 0 : 1]) + ) + + return IR.create_operation( + "vector.transfer_read", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`transfer_write` + +The `vector.transfer_write` op performs a write from a +[vector](../LangRef.md#vector-type), supplied as its first operand, into a +slice within a [MemRef](../LangRef.md#memref-type) or a Ranked +[Tensor](../LangRef.md#tensor-type) of the same base elemental type, +supplied as its second operand. + +A vector memref/tensor operand must have its vector element type match a +suffix (shape and element type) of the vector (e.g. memref<3x2x6x4x3xf32>, +vector<1x1x4x3xf32>). If the operand is a tensor, the operation returns a +new tensor of the same type. + +The slice is further defined by a full-rank index within the MemRef/Tensor, +supplied as the operands `[2 .. 2 + rank(memref/tensor))` that defines the +starting point of the transfer (e.g. `%A[%i0, %i1, %i2, %i3]`). + +The permutation_map [attribute](../LangRef.md#attributes) is an +[affine-map](Affine.md#affine-maps) which specifies the transposition on the +slice to match the vector shape. The permutation map may be implicit and +omitted from parsing and printing if it is the canonical minor identity map +(i.e. if it does not permute any dimension). In contrast to `transfer_read`, +write ops cannot have broadcast dimensions. + +The size of the slice is specified by the size of the vector. + +An optional SSA value `mask` may be specified to mask out elements written +to the MemRef/Tensor. The `mask` type is an `i1` vector with a shape that +matches how elements are written into the MemRef/Tensor, *after* applying +any permutation. Elements whose corresponding mask element is `0` are +masked out. + +For every vector dimension, the boolean array attribute `in_bounds` +specifies if the transfer is guaranteed to be within the source bounds. If +set to \"false\", accesses (including the starting point) may run +out-of-bounds along the respective vector dimension as the index increases. +Non-vector and broadcast dimensions *must* always be in-bounds. The +`in_bounds` array length has to be equal to the vector rank. This attribute +has a default value: `false` (i.e. \"out-of-bounds\"). When skipped in the +textual IR, the default value is assumed. Similarly, the OP printer will +omit this attribute when all dimensions are out-of-bounds (i.e. the default +value is used). + + A `vector.transfer_write` can be lowered to a simple store if all + dimensions are specified to be within bounds and no `mask` was specified. + +This operation is called \'write\' by opposition to \'store\' because the +super-vector granularity is generally not representable with a single +hardware register. A `vector.transfer_write` is thus a +mid-level abstraction that supports super-vectorization with non-effecting +padding for full-tile-only code. It is the responsibility of +`vector.transfer_write`\'s implementation to ensure the memory writes are +valid. Different lowerings may be pertinent depending on the hardware +support. + +# Example + +```mlir +// write vector<16x32x64xf32> into the slice +// `%A[%i0, %i1:%i1+32, %i2:%i2+64, %i3:%i3+16]`: +for %i0 = 0 to %0 { + affine.for %i1 = 0 to %1 step 32 { + affine.for %i2 = 0 to %2 step 64 { + affine.for %i3 = 0 to %3 step 16 { + %val = `ssa-value` : vector<16x32x64xf32> + vector.transfer_write %val, %A[%i0, %i1, %i2, %i3] + {permutation_map: (d0, d1, d2, d3) -> (d3, d1, d2)} : + vector<16x32x64xf32>, memref +}}}} + +// or equivalently (rewrite with vector.transpose) +for %i0 = 0 to %0 { + affine.for %i1 = 0 to %1 step 32 { + affine.for %i2 = 0 to %2 step 64 { + affine.for %i3 = 0 to %3 step 16 { + %val = `ssa-value` : vector<16x32x64xf32> + %valt = vector.transpose %val, [1, 2, 0] : + vector<16x32x64xf32> -> vector<32x64x16xf32> + vector.transfer_write %valt, %A[%i0, %i1, %i2, %i3] + {permutation_map: (d0, d1, d2, d3) -> (d1, d2, d3)} : + vector<32x64x16xf32>, memref +}}}} + +// write to a memref with vector element type. +vector.transfer_write %4, %arg1[%c3, %c3] + {permutation_map = (d0, d1)->(d0, d1)} + : vector<1x1x4x3xf32>, memref> + +// return a tensor where the vector is inserted into the source tensor. +%5 = vector.transfer_write %4, %arg1[%c3, %c3] + {permutation_map = (d0, d1)->(d0, d1)} + : vector<1x1x4x3xf32>, tensor> + +// Special encoding for 0-d transfer with 0-d tensor/memref, vector shape +// {1} and permutation_map () -> (0). +%1 = vector.transfer_write %0, %arg0[] {permutation_map = affine_map<()->(0)>} : + vector<1xf32>, tensor +``` +""" +function transfer_write( + vector::Value, + source::Value, + indices::Vector{Value}, + mask=nothing::Union{Nothing,Value}; + result=nothing::Union{Nothing,IR.Type}, + permutation_map, + in_bounds, + location=Location(), +) + _results = IR.Type[] + _operands = Value[vector, source, indices...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("permutation_map", permutation_map), + namedattribute("in_bounds", in_bounds), + ] + !isnothing(mask) && push!(_operands, mask) + push!( + _attributes, operandsegmentsizes([1, 1, length(indices), isnothing(mask) ? 0 : 1]) + ) + !isnothing(result) && push!(_results, result) + + return IR.create_operation( + "vector.transfer_write", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`transpose` + +Takes a n-D vector and returns the transposed n-D vector defined by +the permutation of ranks in the n-sized integer array attribute (in case +of 0-D vectors the array attribute must be empty). + +In the operation + +```mlir +%1 = vector.transpose %0, [i_1, .., i_n] + : vector + to vector +``` + +the `permutation` array [i_1, .., i_n] must be a permutation of [0, .., n-1]. + +# Example + +```mlir +%1 = vector.transpose %0, [1, 0] : vector<2x3xf32> to vector<3x2xf32> + + [ [a, b, c], [ [a, d], + [d, e, f] ] -> [b, e], + [c, f] ] +``` +""" +function transpose(vector::Value; result::IR.Type, permutation, location=Location()) + _results = IR.Type[result,] + _operands = Value[vector,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("permutation", permutation),] + + return IR.create_operation( + "vector.transpose", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`type_cast` + +Performs a conversion from a memref with scalar element to a memref with a +*single* vector element, copying the shape of the memref to the vector. This +is the minimal viable operation that is required to makeke +super-vectorization operational. It can be seen as a special case of the +`view` operation but scoped in the super-vectorization context. + +# Example + +```mlir +%A = memref.alloc() : memref<5x4x3xf32> +%VA = vector.type_cast %A : memref<5x4x3xf32> to memref> +``` +""" +function type_cast(memref::Value; result::IR.Type, location=Location()) + _results = IR.Type[result,] + _operands = Value[memref,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.type_cast", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`warp_execute_on_lane_0` + +`warp_execute_on_lane_0` is an operation used to bridge the gap between +vector programming and SPMD programming model like GPU SIMT. It allows to +trivially convert a region of vector code meant to run on a multiple threads +into a valid SPMD region and then allows incremental transformation to +distribute vector operations on the threads. + +Any code present in the region would only be executed on first thread/lane +based on the `laneid` operand. The `laneid` operand is an integer ID between +[0, `warp_size`). The `warp_size` attribute indicates the number of lanes in +a warp. + +Operands are vector values distributed on all lanes that may be used by +the single lane execution. The matching region argument is a vector of all +the values of those lanes available to the single active lane. The +distributed dimension is implicit based on the shape of the operand and +argument. the properties of the distribution may be described by extra +attributes (e.g. affine map). + +Return values are distributed on all lanes using laneId as index. The +vector is distributed based on the shape ratio between the vector type of +the yield and the result type. +If the shapes are the same this means the value is broadcasted to all lanes. +In the future the distribution can be made more explicit using affine_maps +and will support having multiple Ids. + +Therefore the `warp_execute_on_lane_0` operations allow to implicitly copy +between lane0 and the lanes of the warp. When distributing a vector +from lane0 to all the lanes, the data are distributed in a block cyclic way. +For exemple `vector<64xf32>` gets distributed on 32 threads and map to +`vector<2xf32>` where thread 0 contains vector[0] and vector[1]. + +During lowering values passed as operands and return value need to be +visible to different lanes within the warp. This would usually be done by +going through memory. + +The region is *not* isolated from above. For values coming from the parent +region not going through operands only the lane 0 value will be accesible so +it generally only make sense for uniform values. + +# Example +``` +// Execute in parallel on all threads/lanes. +vector.warp_execute_on_lane_0 (%laneid)[32] { + // Serial code running only on thread/lane 0. + ... +} +// Execute in parallel on all threads/lanes. +``` + +This may be lowered to an scf.if region as below: +``` + // Execute in parallel on all threads/lanes. + %cnd = arith.cmpi eq, %laneid, %c0 : index + scf.if %cnd { + // Serial code running only on thread/lane 0. + ... + } + // Execute in parallel on all threads/lanes. +``` + +When the region has operands and/or return values: +``` +// Execute in parallel on all threads/lanes. +%0 = vector.warp_execute_on_lane_0(%laneid)[32] +args(%v0 : vector<4xi32>) -> (vector<1xf32>) { +^bb0(%arg0 : vector<128xi32>) : + // Serial code running only on thread/lane 0. + ... + vector.yield %1 : vector<32xf32> +} +// Execute in parallel on all threads/lanes. +``` + +values at the region boundary would go through memory: +``` +// Execute in parallel on all threads/lanes. +... +// Store the data from each thread into memory and Synchronization. +%tmp0 = memreg.alloc() : memref<128xf32> +%tmp1 = memreg.alloc() : memref<32xf32> +%cnd = arith.cmpi eq, %laneid, %c0 : index +vector.store %v0, %tmp0[%laneid] : memref<128xf32>, vector<4xf32> +some_synchronization_primitive +scf.if %cnd { + // Serialized code running only on thread 0. + // Load the data from all the threads into a register from thread 0. This + // allow threads 0 to access data from all the threads. + %arg0 = vector.load %tmp0[%c0] : memref<128xf32>, vector<128xf32> + ... + // Store the data from thread 0 into memory. + vector.store %1, %tmp1[%c0] : memref<32xf32>, vector<32xf32> +} +// Synchronization and load the data in a block cyclic way so that the +// vector is distributed on all threads. +some_synchronization_primitive +%0 = vector.load %tmp1[%laneid] : memref<32xf32>, vector<32xf32> +// Execute in parallel on all threads/lanes. +``` +""" +function warp_execute_on_lane_0( + laneid::Value, + args::Vector{Value}; + results::Vector{IR.Type}, + warp_size, + warpRegion::Region, + location=Location(), +) + _results = IR.Type[results...,] + _operands = Value[laneid, args...] + _owned_regions = Region[warpRegion,] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("warp_size", warp_size),] + + return IR.create_operation( + "vector.warp_execute_on_lane_0", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`yield` + +\"vector.yield\" yields an SSA value from the Vector dialect op region and +terminates the regions. The semantics of how the values are yielded is +defined by the parent operation. +If \"vector.yield\" has any operands, the operands must correspond to the +parent operation\'s results. +If the parent operation defines no value the vector.yield may be omitted +when printing the region. +""" +function yield(operands::Vector{Value}; location=Location()) + _results = IR.Type[] + _operands = Value[operands...,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "vector.yield", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # vector diff --git a/src/Dialects/19/X86Vector.jl b/src/Dialects/19/X86Vector.jl new file mode 100644 index 00000000..c45d602c --- /dev/null +++ b/src/Dialects/19/X86Vector.jl @@ -0,0 +1,499 @@ +module x86vector + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`avx_intr_dp_ps_256` + +""" +function avx_intr_dp_ps_256( + a::Value, b::Value, c::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b, c] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "x86vector.avx.intr.dp.ps.256", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx_intr_dot` + +Computes the 4-way dot products of the lower and higher parts of the source +vectors and broadcasts the two results to the lower and higher elements of +the destination vector, respectively. Adding one element of the lower part +to one element of the higher part in the destination vector yields the full +dot product of the two source vectors. + +# Example + +```mlir +%0 = x86vector.avx.intr.dot %a, %b : vector<8xf32> +%1 = vector.extractelement %0[%i0 : i32]: vector<8xf32> +%2 = vector.extractelement %0[%i4 : i32]: vector<8xf32> +%d = arith.addf %1, %2 : f32 +``` +""" +function avx_intr_dot( + a::Value, b::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "x86vector.avx.intr.dot", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx512_intr_mask_compress` + +""" +function avx512_intr_mask_compress( + a::Value, src::Value, k::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a, src, k] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "x86vector.avx512.intr.mask.compress", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx512_mask_compress` + +The mask.compress op is an AVX512 specific op that can lower to the +`llvm.mask.compress` instruction. Instead of `src`, a constant vector +vector attribute `constant_src` may be specified. If neither `src` nor +`constant_src` is specified, the remaining elements in the result vector are +set to zero. + +#### From the Intel Intrinsics Guide: + +Contiguously store the active integer/floating-point elements in `a` (those +with their respective bit set in writemask `k`) to `dst`, and pass through the +remaining elements from `src`. +""" +function avx512_mask_compress( + k::Value, + a::Value, + src=nothing::Union{Nothing,Value}; + dst=nothing::Union{Nothing,IR.Type}, + constant_src=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[k, a] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(src) && push!(_operands, src) + !isnothing(dst) && push!(_results, dst) + !isnothing(constant_src) && + push!(_attributes, namedattribute("constant_src", constant_src)) + + return IR.create_operation( + "x86vector.avx512.mask.compress", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx512_mask_rndscale` + +The mask.rndscale op is an AVX512 specific op that can lower to the proper +LLVMAVX512 operation: `llvm.mask.rndscale.ps.512` or +`llvm.mask.rndscale.pd.512` instruction depending on the type of vectors it +is applied to. + +#### From the Intel Intrinsics Guide: + +Round packed floating-point elements in `a` to the number of fraction bits +specified by `imm`, and store the results in `dst` using writemask `k` +(elements are copied from src when the corresponding mask bit is not set). +""" +function avx512_mask_rndscale( + src::Value, + k::Value, + a::Value, + imm::Value, + rounding::Value; + dst=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[src, k, a, imm, rounding] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(dst) && push!(_results, dst) + + return IR.create_operation( + "x86vector.avx512.mask.rndscale", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx512_intr_mask_rndscale_pd_512` + +""" +function avx512_intr_mask_rndscale_pd_512( + src::Value, + k::Value, + a::Value, + imm::Value, + rounding::Value; + res=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[src, k, a, imm, rounding] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "x86vector.avx512.intr.mask.rndscale.pd.512", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx512_intr_mask_rndscale_ps_512` + +""" +function avx512_intr_mask_rndscale_ps_512( + src::Value, + k::Value, + a::Value, + imm::Value, + rounding::Value; + res=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[src, k, a, imm, rounding] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "x86vector.avx512.intr.mask.rndscale.ps.512", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx512_mask_scalef` + +The `mask.scalef` op is an AVX512 specific op that can lower to the proper +LLVMAVX512 operation: `llvm.mask.scalef.ps.512` or +`llvm.mask.scalef.pd.512` depending on the type of MLIR vectors it is +applied to. + +#### From the Intel Intrinsics Guide: + +Scale the packed floating-point elements in `a` using values from `b`, and +store the results in `dst` using writemask `k` (elements are copied from src +when the corresponding mask bit is not set). +""" +function avx512_mask_scalef( + src::Value, + a::Value, + b::Value, + k::Value, + rounding::Value; + dst=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[src, a, b, k, rounding] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(dst) && push!(_results, dst) + + return IR.create_operation( + "x86vector.avx512.mask.scalef", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx512_intr_mask_scalef_pd_512` + +""" +function avx512_intr_mask_scalef_pd_512( + src::Value, + a::Value, + b::Value, + k::Value, + rounding::Value; + res=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[src, a, b, k, rounding] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "x86vector.avx512.intr.mask.scalef.pd.512", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx512_intr_mask_scalef_ps_512` + +""" +function avx512_intr_mask_scalef_ps_512( + src::Value, + a::Value, + b::Value, + k::Value, + rounding::Value; + res=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[src, a, b, k, rounding] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "x86vector.avx512.intr.mask.scalef.ps.512", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx_intr_rsqrt_ps_256` + +""" +function avx_intr_rsqrt_ps_256( + a::Value; res=nothing::Union{Nothing,IR.Type}, location=Location() +) + _results = IR.Type[] + _operands = Value[a,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(res) && push!(_results, res) + + return IR.create_operation( + "x86vector.avx.intr.rsqrt.ps.256", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx_rsqrt` + +""" +function avx_rsqrt(a::Value; b=nothing::Union{Nothing,IR.Type}, location=Location()) + _results = IR.Type[] + _operands = Value[a,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(b) && push!(_results, b) + + return IR.create_operation( + "x86vector.avx.rsqrt", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx512_intr_vp2intersect_d_512` + +""" +function avx512_intr_vp2intersect_d_512( + a::Value, b::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "x86vector.avx512.intr.vp2intersect.d.512", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`avx512_vp2intersect` + +The `vp2intersect` op is an AVX512 specific op that can lower to the proper +LLVMAVX512 operation: `llvm.vp2intersect.d.512` or +`llvm.vp2intersect.q.512` depending on the type of MLIR vectors it is +applied to. + +#### From the Intel Intrinsics Guide: + +Compute intersection of packed integer vectors `a` and `b`, and store +indication of match in the corresponding bit of two mask registers +specified by `k1` and `k2`. A match in corresponding elements of `a` and +`b` is indicated by a set bit in the corresponding bit of the mask +registers. +""" +function avx512_vp2intersect( + a::Value, + b::Value; + k1=nothing::Union{Nothing,IR.Type}, + k2=nothing::Union{Nothing,IR.Type}, + location=Location(), +) + _results = IR.Type[] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(k1) && push!(_results, k1) + !isnothing(k2) && push!(_results, k2) + + return IR.create_operation( + "x86vector.avx512.vp2intersect", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=(length(_results) == 0 ? nothing : _results), + result_inference=(length(_results) == 0 ? true : false), + ) +end + +""" +`avx512_intr_vp2intersect_q_512` + +""" +function avx512_intr_vp2intersect_q_512( + a::Value, b::Value; res::IR.Type, location=Location() +) + _results = IR.Type[res,] + _operands = Value[a, b] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "x86vector.avx512.intr.vp2intersect.q.512", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # x86vector diff --git a/src/Dialects/19/XeGPU.jl b/src/Dialects/19/XeGPU.jl new file mode 100644 index 00000000..06e22c09 --- /dev/null +++ b/src/Dialects/19/XeGPU.jl @@ -0,0 +1,734 @@ +module xegpu + +import ...IR: + IR, NamedAttribute, Value, Location, Block, Region, Attribute, context, IndexType +import ..Dialects: namedattribute, operandsegmentsizes + +""" +`alloc_nbarrier` +AllocNbarrier is to create a set of named barriers as + specified by `nbarrier_num`. Named barriers are workgroup level resources, + and are shared by all threads in the workgroup. For example, there are + up to 32 barriers (range 0-31) for each XeCore on PVC. A typical use case + is that a workgroup is partitioned into N subgroups of threads (N <= 32), + and each subgroup coordinating their work with a separate barrier with id + range from 0 to N respectively. +""" +function alloc_nbarrier(; nbarrier_num, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("nbarrier_num", nbarrier_num),] + + return IR.create_operation( + "xegpu.alloc_nbarrier", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`atomic_rmw` + +The `xegpu.atomic_rmw` operation provides a way to perform a read-modify-write +operation on the region described by the `TensorDesc` free from data races. The +`kind` enumeration specifies the modification to be performed, The `mask` operand +has the same shape with `TensorDesc`, and is used to enable or disable specific +data points of the `TensorDesc`. The `value` operand represents the new value to +be applied during the modification. +""" +function atomic_rmw( + tensorDesc::Value, mask::Value, value::Value; result::IR.Type, kind, location=Location() +) + _results = IR.Type[result,] + _operands = Value[tensorDesc, mask, value] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("kind", kind),] + + return IR.create_operation( + "xegpu.atomic_rmw", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_tdesc` + +\"create_tdesc\" is similar to \"create_nd_tdesc\" in terms that it creates +a Tensor Descriptor (TensorDescType) for a memory region. While \"create_nd_tdesc\" +is for creating continuous subviews, \"create_tdesc\" is for creating non-continuous +(scattered) subviews, allowing each work-item in a subgroup specifying their own offset. +It accepts the following parameters: + +* source: a 1D memref or pointer (uint64_t) represents the flattened memory object. +* offsets: a array containing offsets of each access point. Its size + is fixed to the hardware supportted subgroup size, e.g., 16 on PVC, + implying each element in the array corresponds to a work-item (SIMT lane) + in the subgroup. +* chunk_size: [optional attribute] indicates number of continious + elements accessed for each offset, default is 1. + +Example 1. It assumes subgroup size is 4, and accesses a[0], a[16], a[32], a[64] +``` +%a = memref.alloc() : memref<1024xf32> +%1 = xegpu.create_tdesc %a[0, 16, 32, 64]: memref<1024xf32> -> TensorDesc<4xf32> +``` + +Example 2. It assumes subgroup size is 4, and each workitem access 8 elements. + It will access totally 32 data elements: a[0:7], a[16:23], a[32:39], a[64:71] +``` +%0 = memref.alloc() : memref<1024xf32> +%1 = xegpu.create_tdesc %0[0, 16, 32, 64] {chunk_size = 8}: memref<1024xf32> -> TensorDesc<4x8xf32> +``` + +Example 3. It is similar to Example 2, but there is some overlaps among workitems. + It accesses: a[0:7], a[4:11], a[8:15], a[12:19] +``` +%0 = memref.alloc() : memref<1024xf32> +%1 = xegpu.create_tdesc %0[0, 4, 8, 12] {chunk_size = 8}: memref<1024xf32> -> TensorDesc<4x8xf32> +``` +""" +function create_tdesc( + source::Value, + offsets::Vector{Value}; + TensorDesc::IR.Type, + const_offsets, + chunk_size=nothing, + location=Location(), +) + _results = IR.Type[TensorDesc,] + _operands = Value[source, offsets...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("const_offsets", const_offsets),] + !isnothing(chunk_size) && push!(_attributes, namedattribute("chunk_size", chunk_size)) + + return IR.create_operation( + "xegpu.create_tdesc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`create_nd_tdesc` + +The \"create_nd_tdesc\" operation creates a TensorDescType which represents +a sub-view of a 2D memory region (It can be extended to support n-D memory +region if needed in future). Elements in the subview continuous in each +dimension. It encodes the following important information for supporting +Intel hardware features: + +* source: an object representing (starting address/pointer of) a 2D memory region. + It can be either a 2D memref object, or simply a pointer represented by uint64_t type. + for the later case, the shape and layout information of the 2D memory region should + be explicitly passed via `shape` and `strides` parameters. +* offsets: two index values represents offsets from the \"source\" at the each dimension + at which the subview of the target memory will be created. It is encoded via two + variables, including \"offsets\" and \"const_offsets\", such that it can + accept various forms, such as, operands (e.g., [%c0, %c]) and attributes (e.g., [2, 4]). +* shape: the shape information of the memory region pointed by the \"source\". It is + typically encoded via the MemRefType of the source, e.g., memref<4096x4096xf16>. + But if \"source\" is simply a pointer represented as uint64_t type, or a memref + type without shape information e.g., memref, the shape information has + to be explicitly passed via the \"shape\" and \"const_shape\" arguments. +* strides: the strides of the memory region pointed by the \"source\". Similar to shape, + it is typically encoded via the MemRefType of the source too. But if \"source\" is + simply a pointer represented as uint64_t type, or a memref type without shape + information e.g., memref, the strides information has to be explicitly + passed via the \"strides\" and \"const_strides\" argument. + +Example 1 (suppose the tensor shape inferred by the compiler is 8x16): +%0 = memref.alloc() : memref<1024x1024xf32> +%c0 = arith.constant 0 : index +%c1 = arith.constant 1 : index +%1 = xegpu.create_nd_tdesc %0[%c0, %c0]: memref<1024x1024xf32> -> TensorDesc<8x16xf32> + +Example 2 (suppose the tensor shape inferred by the compiler is 8x16): +%0 = memref.alloc(%h, %w) : memref +%c0 = arith.constant 0 : index +%c1 = arith.constant 1 : index +%1 = xegpu.create_nd_tdesc %0[%c0, %c0], [%h, %w], [%w, %c1]: memref -> TensorDesc<8x16xf32> + +Example 3 (suppose the tensor shape inferred by the compiler is 8x16): +%0 = ... : ui64 +%c0 = arith.constant 0 : index +%c1 = arith.constant 1 : index +%1 = xegpu.create_nd_tdesc %0[%c0, %c0], [%h, %w], [%w, %c1]: ui64 -> TensorDesc<8x16xf32> +""" +function create_nd_tdesc( + source::Value, + offsets::Vector{Value}, + shape::Vector{Value}, + strides::Vector{Value}; + TensorDesc::IR.Type, + const_offsets, + const_shape=nothing, + const_strides=nothing, + location=Location(), +) + _results = IR.Type[TensorDesc,] + _operands = Value[source, offsets..., shape..., strides...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("const_offsets", const_offsets),] + push!( + _attributes, + operandsegmentsizes([1, length(offsets), length(shape), length(strides)]), + ) + !isnothing(const_shape) && + push!(_attributes, namedattribute("const_shape", const_shape)) + !isnothing(const_strides) && + push!(_attributes, namedattribute("const_strides", const_strides)) + + return IR.create_operation( + "xegpu.create_nd_tdesc", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`dpas` +DPAS performs matrix multiplication on matrix A of `mxk` + size, B of `kxn` size, and accumulate on matrix C of `mxn` to the same size + matrix , `m=8`, `n=16` and `k=8 * 32/bit_width_of_elem_type`. So for fp16 + data type, the matrices are `A: vector<8x16xf16>`, `B: vector<16x16xf16>`, + and `C/D: vector<8x16xf32>`. Besides the matrix size requirements, DPAS + also requires A and B to be loaded with the required data layout. Specially, + VNNI layout is required for B operand. It is achieved via setting `vnni_axis = 0` + of the corresponding `load_nd` operator. To keep both operands as 3D vector, + operand A is loaded via setting `vnni_axis = 1` without impacting the + physical layouts change in register. Due to the VNNI transformation, A and B operands + are represented as 3D vector, with the last dimension representing the VNNI factor, + which is computed as `32/bit_width_of_elem_type`. Therefore, `A: vector<8x16xf16>` + is represented as `A: vector<8x8x2xf16>`, and `B: vector<16x16xf16>` is + represented as `B: vector<8x16x2xf16>`. + + Note: on PVC, the hardware can perform load with VNNI transformation when data + element type is 16-bit or lower precision, taking 2 or 4 elements from + the first dimension and inserted into the newly added innermost dimension. +""" +function dpas( + lhs::Value, + rhs::Value, + acc=nothing::Union{Nothing,Value}; + result::IR.Type, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[lhs, rhs] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(acc) && push!(_operands, acc) + + return IR.create_operation( + "xegpu.dpas", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`fence` +It synchronizes the memory access between + write and following read or write. + 1. `Memory_kind` describes the memory kind. \"global\" means the global memory, + \"slm\" means the share local memory. + 2. `Fence_scope` describes the scope of fence. \"Workgroup\" means that the scope would be + within each workgroup. \"GPU\" means the scope would be across workgroups within the GPU. +""" +function fence(; memory_kind, fence_scope, location=Location()) + _results = IR.Type[] + _operands = Value[] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[ + namedattribute("memory_kind", memory_kind), + namedattribute("fence_scope", fence_scope), + ] + + return IR.create_operation( + "xegpu.fence", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`init_nbarrier` +InitNbarrierOp assigns the named barrier with the specified + barrier ID (0~31) to the current thread. Multiple threads may bind to the + same named barrier, and the `participant_thread_num` specifies the total + number of threads associated with the nbarrier. It returns an object of + NbarrierType representing the barrier +""" +function init_nbarrier( + nbarrier_id::Value, participant_thread_num::Value; result::IR.Type, location=Location() +) + _results = IR.Type[result,] + _operands = Value[nbarrier_id, participant_thread_num] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "xegpu.init_nbarrier", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`load` + It (aka. load) load data per each work-item. The output + describes the data being loaded at the subgroup level, so its size is + consistent with the number of work-items in a subgroup. When `chunk_size_per_lane` + attribute is larger than 1 in TensorDesc, the output vector will be 2D vector, + with dim-1 correspoding to the chunk size. + + The mask operand masks out memory access so that it is safe to pass out-of-boundary + addresses/offsets as long as they are masked. It applies to slots of SIMD lanes. + + Example: + ``` + %2 = xegpu.load %1, %0 {transpose = [1, 0], + l1_hint = #xegpu.cache_hint, + l2_hint = #xegpu.cache_hint, + l3_hint = #xegpu.cache_hint} + : !xegpu.tensor_desc<16xf32, #xegpu.tdesc_attr>, vector<16xi1> + -> vector<16xf32> + ``` +""" +function load( + TensorDesc::Value, + mask::Value; + value::IR.Type, + transpose=nothing, + l1_hint=nothing, + l2_hint=nothing, + l3_hint=nothing, + location=Location(), +) + _results = IR.Type[value,] + _operands = Value[TensorDesc, mask] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(transpose) && push!(_attributes, namedattribute("transpose", transpose)) + !isnothing(l1_hint) && push!(_attributes, namedattribute("l1_hint", l1_hint)) + !isnothing(l2_hint) && push!(_attributes, namedattribute("l2_hint", l2_hint)) + !isnothing(l3_hint) && push!(_attributes, namedattribute("l3_hint", l3_hint)) + + return IR.create_operation( + "xegpu.load", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`load_nd` + +LoadNdOp essentially mimics the hardware block read instruction to read +a block of data from memory to register. It takes a set of optional cache +hints for each level of cache, L1, L2 and L3. If hardware does not have a +correspoding cache, Corresponding cache hint attribute will be masked. +VNNI transformation is an hardware feature for Intel GPU, which is used to +do data packing during the load for B operand of matrix operation, if +the bit width of the data type is less then 32 bits, e.g., fp16. And +transpose is another Intel hardware feature, which will do transpose +operation when loading the data if the bit width of the data type is +fp32 or fp64. It implies that vnni and transpose cannot exit at the +same time. + +# Example +``` + xegpu.load_nd %1 {transpose = [1, 0], + l1_hint = #xegpu.cache_hint, + l2_hint = #xegpu.cache_hint, + l3_hint = #xegpu.cache_hint} + : !xegpu.tensor_desc<8x16xf32> -> vector<16x8xf32> +``` +""" +function load_nd( + TensorDesc::Value; + value::IR.Type, + vnni_axis=nothing, + transpose=nothing, + l1_hint=nothing, + l2_hint=nothing, + l3_hint=nothing, + location=Location(), +) + _results = IR.Type[value,] + _operands = Value[TensorDesc,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(vnni_axis) && push!(_attributes, namedattribute("vnni_axis", vnni_axis)) + !isnothing(transpose) && push!(_attributes, namedattribute("transpose", transpose)) + !isnothing(l1_hint) && push!(_attributes, namedattribute("l1_hint", l1_hint)) + !isnothing(l2_hint) && push!(_attributes, namedattribute("l2_hint", l2_hint)) + !isnothing(l3_hint) && push!(_attributes, namedattribute("l3_hint", l3_hint)) + + return IR.create_operation( + "xegpu.load_nd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`nbarrier_arrive` +NbarrierArriveOp signals the hardware (or other threads) + that the current thread has produced its data for the consumer threads. When + the hardware signalled by `participant_thread_num` threads for the named barrier, + it will notify the threads waiting for the named barrier to continue their work. +""" +function nbarrier_arrive(nbarrier::Value; location=Location()) + _results = IR.Type[] + _operands = Value[nbarrier,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "xegpu.nbarrier_arrive", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`nbarrier_wait` +NbarrierWaitOp signals the hardware which named barrier + the current thread is waiting for, such that it can get notified when the + named barrier is completed. +""" +function nbarrier_wait(nbarrier::Value; location=Location()) + _results = IR.Type[] + _operands = Value[nbarrier,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + + return IR.create_operation( + "xegpu.nbarrier_wait", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`prefetch_nd` + +It issues an instruction to prefetch a block of data from continuous +memory regions to each level of the cache based on their cache policy. + +# Example +``` + xegpu.prefetch_nd %tdesc {l1_hint = #xegpu.cache_hint, + l2_hint = #xegpu.cache_hint, + l3_hint = #xegpu.cache_hint} + : !xegpu.tensor_desc<8x16xf16> +``` +""" +function prefetch_nd( + TensorDesc::Value; + l1_hint=nothing, + l2_hint=nothing, + l3_hint=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[TensorDesc,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(l1_hint) && push!(_attributes, namedattribute("l1_hint", l1_hint)) + !isnothing(l2_hint) && push!(_attributes, namedattribute("l2_hint", l2_hint)) + !isnothing(l3_hint) && push!(_attributes, namedattribute("l3_hint", l3_hint)) + + return IR.create_operation( + "xegpu.prefetch_nd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`prefetch` + +It issues instructions to prefetch a set of scattered data points +from memory to each level of the cache based on their cache policy. +As compared to prefetch_nd, which works on non-scattered TensorDesc, +it works on scattered TensorDesc instead. + +# Example +``` + xegpu.prefetch %tdesc {l1_hint = #xegpu.cache_hint, + l2_hint = #xegpu.cache_hint, + l3_hint = #xegpu.cache_hint} + : !xegpu.tensor_desc<16xf16> +``` +""" +function prefetch( + TensorDesc::Value; + l1_hint=nothing, + l2_hint=nothing, + l3_hint=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[TensorDesc,] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(l1_hint) && push!(_attributes, namedattribute("l1_hint", l1_hint)) + !isnothing(l2_hint) && push!(_attributes, namedattribute("l2_hint", l2_hint)) + !isnothing(l3_hint) && push!(_attributes, namedattribute("l3_hint", l3_hint)) + + return IR.create_operation( + "xegpu.prefetch", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`store_nd` + +StoreNdOp essentially mimics the hardware block write instruction io +write a block of data from register into the memory region as described +by the TensorDesc. It takes a set of optional cache hints for each level +of cache, L1, L2 and L3. If hardware does not have a correspoding cache, +Corresponding cache hint attribute will be masked. + +# Example +``` + xegpu.store_nd %3, %2 {l1_hint = #xegpu.cache_hint, + l2_hint = #xegpu.cache_hint, + l3_hint = #xegpu.cache_hint} + : vector<8x16xf16>, !xegpu.tensor_desc<8x16xf16> +``` +""" +function store_nd( + value::Value, + TensorDesc::Value; + l1_hint=nothing, + l2_hint=nothing, + l3_hint=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, TensorDesc] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(l1_hint) && push!(_attributes, namedattribute("l1_hint", l1_hint)) + !isnothing(l2_hint) && push!(_attributes, namedattribute("l2_hint", l2_hint)) + !isnothing(l3_hint) && push!(_attributes, namedattribute("l3_hint", l3_hint)) + + return IR.create_operation( + "xegpu.store_nd", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`store` + It (aka. store) stores data to scattered memory locations. + It has similar semantic to `load_gather`. + + Example: + ``` + %3 = xegpu.store %0, %1, %2 {l1_hint = #xegpu.cache_hint, + l2_hint = #xegpu.cache_hint, + l3_hint = #xegpu.cache_hint} + : vector<16xf32>, !xegpu.tensor_desc<16xf32, #xegpu.tdesc_attr>, vector<16xi1> + ``` +""" +function store( + value::Value, + TensorDesc::Value, + mask::Value; + l1_hint=nothing, + l2_hint=nothing, + l3_hint=nothing, + location=Location(), +) + _results = IR.Type[] + _operands = Value[value, TensorDesc, mask] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[] + !isnothing(l1_hint) && push!(_attributes, namedattribute("l1_hint", l1_hint)) + !isnothing(l2_hint) && push!(_attributes, namedattribute("l2_hint", l2_hint)) + !isnothing(l3_hint) && push!(_attributes, namedattribute("l3_hint", l3_hint)) + + return IR.create_operation( + "xegpu.store", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`update_nd_offset` +The op updates the offset of the given TensorDesc. + The offsets are relative offset to the current position in the number + of elements. It will result in a same type TensorDesc as the input. + + example: + ``` + %2 = xegpu.update_nd_offset %1, [0, 16]: !xegpu.tensor_desc<8x16xf32> + ``` +""" +function update_nd_offset( + TensorDesc::Value, + offsets::Vector{Value}; + result::IR.Type, + const_offsets, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[TensorDesc, offsets...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("const_offsets", const_offsets),] + + return IR.create_operation( + "xegpu.update_nd_offset", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +""" +`update_offset` +It behaves similar to `update_nd_offset` in terms that + it updates offset of a TensorDesc, and the offsets are relative offset to + the current position in the number of elements. However, `update_nd_offset` + is to update the start point of a 2D block, so its offset constains two + elements representing the shift in each dimension. `update_offset` is to + update the offset per work-item, so its offsets contains values representing + shifts for each work-item. + + Example: + ``` + %2 = xegpu.update_offset %1, [32, 32, 32, 32] + : !xegpu.tensor_desc<4x2xf32, #xegpu.tdesc_attr> + ``` +""" +function update_offset( + TensorDesc::Value, + offsets::Vector{Value}; + result::IR.Type, + const_offsets, + location=Location(), +) + _results = IR.Type[result,] + _operands = Value[TensorDesc, offsets...] + _owned_regions = Region[] + _successors = Block[] + _attributes = NamedAttribute[namedattribute("const_offsets", const_offsets),] + + return IR.create_operation( + "xegpu.update_offset", + location; + operands=_operands, + owned_regions=_owned_regions, + successors=_successors, + attributes=_attributes, + results=_results, + result_inference=false, + ) +end + +end # xegpu diff --git a/src/Dialects/Dialects.jl b/src/Dialects/Dialects.jl index 091fb61a..98bc2349 100644 --- a/src/Dialects/Dialects.jl +++ b/src/Dialects/Dialects.jl @@ -1,5 +1,7 @@ module Dialects +using ..MLIR: MLIR_VERSION_MIN, MLIR_VERSION_MAX + include("Utils.jl") # generate versioned modules @@ -17,7 +19,7 @@ end begin # list dialect operations - local dialectops = mapreduce(mergewith!(∪), [v14, v15, v16, v17, v18]) do mod + local dialectops = mapreduce(mergewith!(∪), [v14, v15, v16, v17, v18, v19]) do mod dialects = filter(names(mod; all=true)) do dialect dialect ∉ [nameof(mod), :eval, :include] && !startswith(string(dialect), '#') end @@ -33,11 +35,11 @@ begin for (dialect, ops) in dialectops mod = @eval module $dialect using ...MLIR: MLIR_VERSION, MLIRException - using ..Dialects: v14, v15, v16, v17, v18 + using ..Dialects: v14, v15, v16, v17, v18, v19 end for op in ops - container_mods = filter([v14, v15, v16, v17, v18]) do mod + container_mods = filter([v14, v15, v16, v17, v18, v19]) do mod dialect in names(mod; all=true) && op in names(getproperty(mod, dialect); all=true) end @@ -47,7 +49,7 @@ begin @eval mod function $op(args...; kwargs...) version = MLIR_VERSION[] - if v"14" > version > v"18" + if !($MLIR_VERSION_MIN <= version <= $MLIR_VERSION_MAX) error("Unsupported MLIR version $version") end diff --git a/src/MLIR.jl b/src/MLIR.jl index f718b587..7a737f10 100644 --- a/src/MLIR.jl +++ b/src/MLIR.jl @@ -9,6 +9,9 @@ const MLIR_VERSION = ScopedValue( ) const MLIR_C_PATH = ScopedValue(@load_preference("MLIR_C_PATH", MLIR_jll.mlir_c)) +const MLIR_VERSION_MIN = v"14" +const MLIR_VERSION_MAX = v"19" + struct MLIRException <: Exception msg::String end