From fbd17fbed2cf77c325fdd6d1958e51f269a21d65 Mon Sep 17 00:00:00 2001 From: Sergey Lyalin Date: Sat, 15 Jun 2024 19:41:07 +0000 Subject: [PATCH 001/121] Initial integration of MLIR into OpenVINO core. - MLIR is used is a new ngraph transformation, compiled together with other transformations and called from CPU plugin transformation pipeline. - The transformation identifies OV Add operation in the graph and replaces it by a new custom MLIROp operation -- a single op to enclose arbitrary MLIR program. - Add op lowering uses linalg::AddOp on tensors. Each op is represented as isolated MLIR Module. - MLIROp::evaluate calls MLIR-compiled partition when graph is inferred. - Limitation: Add op should have no implicit broadcast, it is not supported and not checked. If Add implies implicit broadcast the result is undefined. - Short code to activate the functionality (in Python, using PyTorch as a source for a small model): import torch import openvino as ov class My(torch.nn.Module): def forward(self, a): b = a*a return ((a+a) * (a+b)) / a my = My() input = torch.tensor([1, 2, 3], dtype=torch.float32) print('Expected:', my(input)) ov_model = ov.convert_model(my, example_input=input) print(ov_model) ov_compiled = ov.compile_model(ov_model) print('Result:', ov_compiled(input)[0]) --- src/cmake/openvino.cmake | 20 + src/common/transformations/CMakeLists.txt | 8 +- .../include/transformations/mlir/convert.hpp | 17 + .../src/transformations/mlir/convert.cpp | 704 ++++++++++++++++++ .../transformation_pipeline.cpp | 3 + 5 files changed, 751 insertions(+), 1 deletion(-) create mode 100644 src/common/transformations/include/transformations/mlir/convert.hpp create mode 100644 src/common/transformations/src/transformations/mlir/convert.cpp diff --git a/src/cmake/openvino.cmake b/src/cmake/openvino.cmake index ef9be523e6e794..4904eb6b2c66d2 100644 --- a/src/cmake/openvino.cmake +++ b/src/cmake/openvino.cmake @@ -49,11 +49,31 @@ target_include_directories(${TARGET_NAME} INTERFACE $ $) +find_package(MLIR REQUIRED CONFIG) + +set(MLIR_OPENVINO_LIBS + MLIRAnalysis + MLIRExecutionEngine + MLIRIR + MLIRJitRunner + MLIRLLVMDialect + MLIRLLVMToLLVMIRTranslation + MLIRToLLVMIRTranslationRegistration + MLIRParser + MLIRTargetLLVMIRExport + MLIRSupport + MLIROptLib + LLVMX86AsmParser + MLIRFuncDialect + MLIRFuncAllExtensions + MLIRUBToLLVM) + target_link_libraries(${TARGET_NAME} PRIVATE openvino::reference openvino::shape_inference openvino::pugixml ${CMAKE_DL_LIBS} + ${MLIR_OPENVINO_LIBS} Threads::Threads PUBLIC $<$,$,9.1>>:stdc++fs> $<$,$,9.0>>:c++fs>) diff --git a/src/common/transformations/CMakeLists.txt b/src/common/transformations/CMakeLists.txt index e51446beccb6cc..3d177f4c10eda0 100644 --- a/src/common/transformations/CMakeLists.txt +++ b/src/common/transformations/CMakeLists.txt @@ -9,6 +9,8 @@ set(PUBLIC_HEADERS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") file(GLOB_RECURSE LIBRARY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) file(GLOB_RECURSE PUBLIC_HEADERS ${PUBLIC_HEADERS_DIR}/*.hpp) +find_package(MLIR REQUIRED CONFIG) + # Create named folders for the sources within the .vcproj # Empty name lists them directly under the .vcproj @@ -24,10 +26,14 @@ ov_build_target_faster(${TARGET_NAME}_obj PCH_HEADER "src/precomp.hpp" ) +target_compile_features(${TARGET_NAME}_obj PUBLIC cxx_std_17) + target_link_libraries(${TARGET_NAME}_obj PRIVATE openvino::reference openvino::itt openvino::core::dev openvino::shape_inference) target_include_directories(${TARGET_NAME}_obj PRIVATE "${PUBLIC_HEADERS_DIR}" - "${CMAKE_CURRENT_SOURCE_DIR}/src") + "${CMAKE_CURRENT_SOURCE_DIR}/src" + "${MLIR_INCLUDE_DIRS}" + "${LLVM_INCLUDE_DIRS}") ov_add_clang_format_target(${TARGET_NAME}_clang FOR_TARGETS ${TARGET_NAME}_obj) diff --git a/src/common/transformations/include/transformations/mlir/convert.hpp b/src/common/transformations/include/transformations/mlir/convert.hpp new file mode 100644 index 00000000000000..5fbcc3d13e2a6d --- /dev/null +++ b/src/common/transformations/include/transformations/mlir/convert.hpp @@ -0,0 +1,17 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/core/model.hpp" +#include "transformations_visibility.hpp" + +namespace ov { + +namespace pass { + +void TRANSFORMATIONS_API transformMLIR(std::shared_ptr model); + +} +} diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp new file mode 100644 index 00000000000000..220a6dfdc37b33 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -0,0 +1,704 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "transformations/mlir/convert.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "itt.hpp" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "mlir/Dialect/Arith/IR/Arith.h" +#include "mlir/Dialect/Arith/Transforms/Passes.h" +#include "mlir/Dialect/Bufferization/Transforms/Passes.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Linalg/TransformOps/DialectExtension.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/Dialect/Tensor/TransformOps/TensorTransformOps.h" +#include "mlir/Dialect/Vector/IR/VectorOps.h" +#include "mlir/ExecutionEngine/ExecutionEngine.h" +#include "mlir/ExecutionEngine/JitRunner.h" +#include "mlir/ExecutionEngine/OptUtils.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/Dialect.h" +#include "mlir/IR/Location.h" +#include "mlir/IR/ValueRange.h" +#include "mlir/InitAllDialects.h" +#include "mlir/InitAllExtensions.h" +#include "mlir/InitAllPasses.h" +#include "mlir/Parser/Parser.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Support/LLVM.h" +#include "mlir/Target/LLVMIR/Dialect/All.h" +#include "mlir/Target/LLVMIR/Export.h" +#include "mlir/Target/LLVMIR/ModuleTranslation.h" +#include "openvino/core/dimension.hpp" +#include "openvino/core/rt_info.hpp" +#include "openvino/pass/pattern/op/wrap_type.hpp" +#include "transformations_visibility.hpp" + +using namespace mlir; + + +static void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module) { + // A set of default passes that lower any input IR to LLVM + PassManager pm(module->getContext()); + +#if 0 // TODO: if TPP is available + + tpp::DefaultPipelineOptions defPipelineOpts{defGpuBackend}; + pm.addPass(tpp::createDefaultPipeline(defPipelineOpts)); + +#else // Simplified default lowering to LLVM from LLVM tests + + pm.addPass(bufferization::createOneShotBufferizePass()); + + // Blanket-convert any remaining high-level vector ops to loops if any remain. + pm.addNestedPass(createConvertVectorToSCFPass()); + // pm.addNestedPass(createLinalgGeneralizeNamedOpsPass()); + // Blanket-convert any remaining linalg ops to loops if any remain. + pm.addNestedPass(createConvertLinalgToLoopsPass()); + // Blanket-convert any remaining affine ops if any remain. + pm.addPass(createLowerAffinePass()); + // Convert SCF to CF (always needed). + pm.addPass(createConvertSCFToCFPass()); + // Sprinkle some cleanups. + pm.addPass(createCanonicalizerPass()); + pm.addPass(createCSEPass()); + // Blanket-convert any remaining linalg ops to LLVM if any remain. + // pm.addPass(createConvertLinalgToLLVMPass()); // no such pass + // Convert vector to LLVM (always needed). + pm.addPass(createConvertVectorToLLVMPass()); + // Convert Math to LLVM (always needed). + pm.addNestedPass(createConvertMathToLLVMPass()); + // Expand complicated MemRef operations before lowering them. + pm.addPass(memref::createExpandStridedMetadataPass()); + // The expansion may create affine expressions. Get rid of them. + pm.addPass(createLowerAffinePass()); + // Convert MemRef to LLVM (always needed). + // pm.addPass(memref::createExpandOpsPass()); + pm.addPass(createFinalizeMemRefToLLVMConversionPass()); + // Convert Func to LLVM (always needed). + pm.addPass(createConvertFuncToLLVMPass()); + // Convert Index to LLVM (always needed). + pm.addPass(createConvertIndexToLLVMPass()); + // Convert remaining unrealized_casts (always needed). + pm.addPass(createReconcileUnrealizedCastsPass()); + +#endif + + auto result = pm.run(module.get()); + if (failed(result)) { + llvm::errs() << "ERROR: Failed to lower IR to LLVM dialect\n"; + module->print(llvm::errs()); + } +} + +std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext& llvmContext) { + // Default lowering for mlir-cpu-runner + auto llvmModule = translateModuleToLLVMIR(module, llvmContext); + assert(llvmModule); + + // Target machine, null if not specified + std::unique_ptr targetMachine; + + std::string triple = "x86_64-linux-gnu"; + std::string cpuName = "alderlake"; // sapphirerapids, nehalem, etc. + std::string fpuName = "avx2"; // sse4.2, avx, avx2, avx512bf16, etc. + bool printLLVM = false; + auto codeGenOpt = 2; + + // Specify target machine + if (!triple.empty() && !cpuName.empty()) { + std::string error; + const llvm::Target* target = llvm::TargetRegistry::lookupTarget(triple, error); + if (!target) { + llvm::errs() << "Error while looking up target triple: "; + llvm::errs() << error << "\n"; + return nullptr; + } + + // These options should force fused MLA, but they don't. :/ + // Adding unsafe math attribute to functions below do the trick. + llvm::TargetOptions targetOptions; + targetOptions.UnsafeFPMath = true; + targetOptions.AllowFPOpFusion = llvm::FPOpFusion::FPOpFusionMode::Fast; + targetMachine.reset(target->createTargetMachine(triple, + cpuName, + "+" + fpuName, + targetOptions, + /* reloc model */ std::nullopt, + /* code model */ std::nullopt, + llvm::CodeGenOptLevel(codeGenOpt))); + if (!targetMachine) { + llvm::errs() << "Error while looking up target CPU: "; + llvm::errs() << cpuName << "\n"; + return nullptr; + } + } + + // Run the optimized pipeline + int sizeLevel = 0; + auto optPipeline = makeOptimizingTransformer(codeGenOpt, sizeLevel, targetMachine.get()); + if (auto err = optPipeline(llvmModule.get())) { + llvmModule->print(llvm::errs(), nullptr); + llvm::errs() << "Error while passing through the LLVM pipeline: "; + llvm::errs() << err << "\n"; + return nullptr; + } + + // MLIR doesn't lower LLVM with fast-math flags, but we need that, so we + // add for each function, to get FMAs and other goodies. + for (auto& func : llvmModule->functions()) { + func.addFnAttr("unsafe-fp-math", "true"); + } + + if (printLLVM) + llvmModule->print(llvm::outs(), nullptr); + + return llvmModule; +} + + +// TODO: u4/i4 types are not supported +struct MemRef { + MemRef() = default; + + MemRef(ov::Tensor tensor) + : allocated(tensor.data()), + aligned(tensor.data()), + offset(0), + shape(tensor.get_shape().begin(), tensor.get_shape().end()) { + strides.resize(tensor.get_shape().size()); + const auto& byte_strides = tensor.get_strides(); + auto element_size = tensor.get_element_type().size(); + for (size_t i = 0; i < strides.size(); ++i) { + assert(byte_strides[i] % element_size == 0); + // TODO: handle case when stride is not aligned (restrict at OV API level) + strides[i] = byte_strides[i] / element_size; + //std::cout << "stride [" << i << "] = " << strides[i] << "\n"; + } + } + + void* allocated; + void* aligned; + int64_t offset; + std::vector shape; + std::vector strides; + + void append_to_packed_args(std::vector& args) { + args.push_back(&allocated); + args.push_back(&aligned); + args.push_back(&offset); + for (size_t i = 0; i < shape.size(); ++i) { + args.push_back(&shape[i]); + } + for (size_t i = 0; i < strides.size(); ++i) { + args.push_back(&strides[i]); + } + } +}; + +class MLIREvaluate { + OwningOpRef module; // FIXME: needs to be kept? + std::unique_ptr engine; + +public: + MLIREvaluate(OwningOpRef _module) : module(std::move(_module)) { + if (true) { + std::cout << "[ DEBUG ] Source MLIR:\n"; + std::cerr << "-----------------------------------------\n"; + module->dump(); + std::cout << "-----------------------------------------\n"; + } + + prepareMLIRKernelWithoutWrapper(module); + + if (true) { + std::cerr << "[ DEBUG ] Target LLVM:\n"; + std::cerr << "-----------------------------------------\n"; + module->dump(); + std::cerr << "-----------------------------------------\n"; + } + + auto optPipeline = mlir::makeOptimizingTransformer(2, + /*sizeLevel=*/0, // FIXME: HARDCODED + /*targetMachine=*/nullptr); + + mlir::ExecutionEngineOptions engineOptions; + engineOptions.transformer = optPipeline; // opt level looks to be overriden in lowerToLLVMIR, but is still used + // in `create` independently + engineOptions.llvmModuleBuilder = lowerToLLVMIR; + auto maybeEngine = mlir::ExecutionEngine::create(module.get(), engineOptions); + assert(maybeEngine && "failed to construct an execution engine"); + engine = std::move(maybeEngine.get()); + } + + bool invoke_packed(std::vector& args) { + auto invocationResult = engine->invokePacked("entry", args); + if (invocationResult) { + llvm::errs() << "JIT invocation failed\n"; + return false; + } + return true; + } +}; + +typedef std::vector> OVOutputTypes; + +class OPENVINO_API MLIROp : public ov::op::Op { + std::shared_ptr engine; + OVOutputTypes output_types; + +public: + OPENVINO_OP("MLIROp"); + + MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types) + : Op(args), + engine(engine), + output_types(output_types) { + constructor_validate_and_infer_types(); + } + + void validate_and_infer_types() override { + set_output_size(output_types.size()); + for (size_t i = 0; i < output_types.size(); ++i) { + set_output_type(i, std::get<0>(output_types[i]), std::get<1>(output_types[i])); + } + } + + std::shared_ptr clone_with_new_inputs(const ov::OutputVector& new_args) const override { + return std::make_shared(new_args, engine, output_types); + } + + bool evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const override { + // FIXME: Assumes an output contains only one tensor which shape is just propagated from inputs[0]. + // TODO: Involve internal for a partition shape propagation or rely on symbolic shape info. + outputs[0].set_shape(inputs[0].get_shape()); + + std::vector memref_args; + for (size_t i = 0; i < inputs.size(); ++i) { + memref_args.push_back(MemRef(inputs[i])); + } + for (size_t i = 0; i < outputs.size(); ++i) { + memref_args.push_back(MemRef(outputs[i])); + } + std::vector args; + + std::for_each(memref_args.begin(), memref_args.end(), [&args](MemRef& x) { + x.append_to_packed_args(args); + }); + + std::cerr << "[ INFO ] Running kernel in MLIROp::evaluate\n"; + return engine->invoke_packed(args); + } + + bool has_evaluate() const override { + return true; + } +}; + +mlir::Location createLayerLocation(mlir::MLIRContext* ctx, const std::string& layerName, const std::string& layerType) { + const auto layerNameAttr = mlir::StringAttr::get(ctx, layerName); + const auto nameLoc = mlir::NameLoc::get(layerNameAttr); + + SmallVector fields; + fields.emplace_back(mlir::StringAttr::get(ctx, "type"), mlir::StringAttr::get(ctx, layerType)); + fields.emplace_back(mlir::StringAttr::get(ctx, "name"), layerNameAttr); + auto metadata = mlir::DictionaryAttr::get(ctx, fields); + + return mlir::FusedLoc::get(ctx, {nameLoc}, metadata); +} + +SmallVector importShape(const ov::PartialShape& shape) { + SmallVector out(shape.rank().get_length()); + // TODO: Add support for dynamically ranked shapes + for (size_t i = 0; i < out.size(); ++i) { + const auto& dim = shape[i]; + out[i] = dim.is_static() ? dim.get_length() : mlir::ShapedType::kDynamic; + } + return out; +} + +mlir::IntegerType getInt1Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 1); +} + +mlir::IntegerType getInt4Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 4); +} + +mlir::IntegerType getInt8Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 8); +} + +mlir::IntegerType getInt16Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 16); +} + +mlir::IntegerType getInt32Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 32); +} + +mlir::IntegerType getInt64Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 64); +} + +mlir::IntegerType getSInt4Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 4, mlir::IntegerType::Signed); +} + +mlir::IntegerType getSInt8Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Signed); +} + +mlir::IntegerType getSInt16Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 16, mlir::IntegerType::Signed); +} + +mlir::IntegerType getSInt32Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 32, mlir::IntegerType::Signed); +} + +mlir::IntegerType getSInt64Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 64, mlir::IntegerType::Signed); +} + +mlir::IntegerType getUInt4Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 4, mlir::IntegerType::Unsigned); +} + +mlir::IntegerType getUInt8Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Unsigned); +} + +mlir::IntegerType getUInt16Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 16, mlir::IntegerType::Unsigned); +} + +mlir::IntegerType getUInt32Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 32, mlir::IntegerType::Unsigned); +} + +mlir::IntegerType getUInt64Type(mlir::MLIRContext* ctx) { + return mlir::IntegerType::get(ctx, 64, mlir::IntegerType::Unsigned); +} + +mlir::IntegerType getBool8Type(mlir::MLIRContext* ctx) { + // Signless 8-bit integer use for BOOL, to distinguish it from U8 + return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Signless); +} + +mlir::Type importPrecision(mlir::MLIRContext* ctx, const ov::element::Type& precision) { + switch (precision) { + case ov::element::Type_t::f64: + return mlir::Float64Type::get(ctx); + case ov::element::Type_t::f32: + return mlir::Float32Type::get(ctx); + case ov::element::Type_t::f16: + return mlir::Float16Type::get(ctx); + case ov::element::Type_t::bf16: + return mlir::BFloat16Type::get(ctx); + case ov::element::Type_t::i64: + return getSInt64Type(ctx); + case ov::element::Type_t::u64: + return getUInt64Type(ctx); + case ov::element::Type_t::i32: + return getSInt32Type(ctx); + case ov::element::Type_t::u32: + return getUInt32Type(ctx); + case ov::element::Type_t::i16: + return getSInt16Type(ctx); + case ov::element::Type_t::u16: + return getUInt16Type(ctx); + case ov::element::Type_t::i8: + return getSInt8Type(ctx); + case ov::element::Type_t::u8: + return getUInt8Type(ctx); + case ov::element::Type_t::i4: + return getSInt4Type(ctx); + case ov::element::Type_t::u4: + return getUInt4Type(ctx); + case ov::element::Type_t::boolean: + return getBool8Type(ctx); + default: + OPENVINO_THROW("Unsupported element_type: ", precision); + } +} + +mlir::RankedTensorType importTensor(mlir::MLIRContext* ctx, + const ov::PartialShape& shape, + const ov::element::Type& elemType) { + return mlir::RankedTensorType::get(ArrayRef(importShape(shape)), importPrecision(ctx, elemType)); +} + +mlir::Location createLocation(mlir::MLIRContext* ctx, std::shared_ptr node) { + return createLayerLocation(ctx, node->get_friendly_name(), node->get_type_name()); +} + +namespace std { + +template +size_t getHash(const T& val) { + return std::hash()(val); +} + +template +size_t getHash(const T& val, Args&&... args) { + return llvm::hash_combine(getHash(val), getHash(std::forward(args)...)); +} + +template <> +struct hash> final { + size_t operator()(const ov::Output& out) const { + return getHash(out.get_node(), out.get_index()); + } +}; +} // namespace std + + +MemRefType convertTensorToMemRef(TensorType tensorType) { + ArrayRef shape = tensorType.getShape(); + Type elementType = tensorType.getElementType(); + return MemRefType::get(shape, elementType); +} + + +SmallVector tensorsToMemRefs(SmallVector tensors) { + SmallVector out; + out.reserve(tensors.size()); + for (const auto& tensor : tensors) { + out.push_back(convertTensorToMemRef(dyn_cast(tensor))); + } + return out; +} + + +SmallVector get_types_for_values(mlir::MLIRContext* context, const ov::OutputVector& values) { + SmallVector types; + types.reserve(values.size()); + for (const auto& output : values) { + types.push_back(importTensor(context, output.get_partial_shape(), output.get_element_type())); + } + return types; +} + +class ConversionContext { +public: + using Convertor = std::function)>; + using NodeOutputMap = std::unordered_map, mlir::Value>; + + static const std::map convertors; + mlir::MLIRContext* context; + mlir::OpBuilder* block_builder; + NodeOutputMap nodeOutputMap; + + ConversionContext(mlir::MLIRContext* context, mlir::OpBuilder* block_builder) + : context(context), + block_builder(block_builder) {} + + SmallVector getInputs(std::shared_ptr node) { + SmallVector out; + out.reserve(node->get_input_size()); + for (const auto& input : node->inputs()) { + out.push_back(nodeOutputMap.at(input.get_source_output())); + } + return out; + } + + void addOutputs(std::shared_ptr node, mlir::Operation* op) { + const auto results = op->getOpResults(); + + OPENVINO_ASSERT( + results.size() == node->get_output_size(), + "Mismatch between original Node '{0}' number of outputs '{1}' and created number of outputs '{2}'", + node->get_friendly_name(), + node->get_output_size(), + results.size()); + + for (const auto& res : results) { + nodeOutputMap.emplace(node->output(res.getResultNumber()), res); + } + } + + mlir::OpBuilder& builder() { + return *block_builder; + } + + void convert(std::shared_ptr node) { + convertors.at(node->get_type_info())(*this, node); + } +}; + +template +struct ConvertBinary { + void operator()(ConversionContext& context, std::shared_ptr node) { + auto loc = createLocation(context.context, node); + // TODO: Support broadcasts + const auto inputs = context.getInputs(node); + auto op = context.builder().create(loc, + mlir::ValueRange{inputs[0], inputs[1]}, + /* FIXME: Use linalg.fill or tensor.empty */ mlir::ValueRange{inputs[0]}); + context.addOutputs(node, op); + } +}; + +const std::map ConversionContext::convertors = { + {ov::op::v1::Add::get_type_info_static(), Convertor(ConvertBinary())}}; + +mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, + const ov::OutputVector& inputs, + const ov::NodeVector& nodes, + const ov::OutputVector& outputs) { + auto inputTypes = tensorsToMemRefs(get_types_for_values(context, inputs)); + auto outputTypes = tensorsToMemRefs(get_types_for_values(context, outputs)); + + const auto moduleLoc = createLayerLocation(context, "module", "Module"); + auto module = mlir::ModuleOp::create(moduleLoc, StringRef("fragment_name")); + + auto moduleBuilder = mlir::OpBuilder::atBlockBegin(module.getBody() /* TODO: building log here */); + + const auto funcLoc = createLayerLocation(context, "entry", "Func"); + auto memref_args = inputTypes; + memref_args.append(outputTypes); + const auto funcType = mlir::FunctionType::get(context, ArrayRef(memref_args), ArrayRef(SmallVector())); + auto func = moduleBuilder.create(funcLoc, "entry", funcType); + auto block_builder = mlir::OpBuilder::atBlockBegin(func.addEntryBlock() /* TODO: Add logger here */); + + ConversionContext conversion_context(context, &block_builder); + + for (size_t i = 0; i < inputs.size(); ++i) { + auto funcInputVal = func.getArgument(i); + // transition from memref enclosure to tensor interior + auto loc = createLocation(context, inputs[i].get_node_shared_ptr()); + auto tensor = block_builder.create(loc, funcInputVal, /*restrict = */ true); + conversion_context.nodeOutputMap.emplace(inputs[i], tensor); + } + + for (size_t i = 0; i < nodes.size(); ++i) { + auto node = nodes[i]; + conversion_context.convert(node); + } + + SmallVector funcOutputs; + funcOutputs.reserve(outputs.size()); + + for (size_t i = 0; i < outputs.size(); ++i) { + auto tensor = conversion_context.nodeOutputMap.at(outputs[i]); + auto memref = func.getArgument(i + inputs.size()); + auto loc = createLocation(context, outputs[i].get_node_shared_ptr()); + auto materialize = block_builder.create(loc, tensor, memref); + materialize.setWritable(true); // TODO: Can I set it as ctor argument above? + } + + const auto retLoc = createLayerLocation(context, "output", "Output"); + block_builder.create(retLoc, ArrayRef(SmallVector())); + + return module; +} + +class AddLowering : public ov::pass::MatcherPass { +public: + OPENVINO_RTTI("AddLowering", "0"); + explicit AddLowering(mlir::MLIRContext* context) { + auto pattern = ov::pass::pattern::wrap_type( + {ov::pass::pattern::any_input(), ov::pass::pattern::any_input()}); + + auto callback = [=, context](ov::pass::pattern::Matcher& m) { + std::cout << "[ INFO ] Matched AddLowering\n"; + auto add = m.get_match_root(); + + mlir::OwningOpRef module; + + // FIXME: Suppose no broadcast + module = ngraph_to_mlir(context, add->input_values(), {add}, add->outputs()); + + auto expected_outputs = add->outputs(); + OVOutputTypes output_types; + for (size_t i = 0; i < expected_outputs.size(); ++i) { + output_types.push_back( + std::make_tuple(expected_outputs[i].get_element_type(), expected_outputs[i].get_partial_shape())); + } + auto replacement = std::make_shared(add->input_values(), + std::make_shared(std::move(module)), + output_types); + + replace_node(add, replacement); + return true; + }; + + auto m = std::make_shared(pattern, "AddLowering"); + register_matcher(m, callback); + } +}; + + +void injectMLIR(std::shared_ptr model, MLIRContext* context) { + ov::pass::Manager manager; + manager.set_per_pass_validation(true); + manager.register_pass(context); + manager.run_passes(model); +} + + +MLIRContext* get_shared_mlir_context() { + // Gives MLIRContext instance shared for entire OV process and initialized once upon the initial request + // FIXME: Bind with OpenVINO lifetime in the sutable class instead of dirty tricking with static lifetime + + static std::shared_ptr context; + + if (!context) { + + // Initialize the LLVM machinery + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + + // Initialize GPU-related LLVM machinery + // tpp::initializeGpuTargets(); + + // Add the following to include *all* MLIR Core dialects, or selectively + // include what you need like above. You only need to register dialects that + // will be *parsed* by the tool, not the one generated + DialectRegistry registry; + // registry.insert(); + // registry.insert(); + // registry.insert(); + + registerAllDialects(registry); + registerAllExtensions(registry); + registerAllToLLVMIRTranslations(registry); + mlir::linalg::registerTransformDialectExtension(registry); + mlir::tensor::registerTransformDialectExtension(registry); + + context = std::make_shared(registry); + + context->loadDialect(); + context->loadDialect(); + context->loadDialect(); + } + + return context.get(); +} + +void ov::pass::transformMLIR(std::shared_ptr model) { + injectMLIR(model, get_shared_mlir_context()); +} diff --git a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp index 36dc04bc3b2ce7..c35fdb828c12ca 100644 --- a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp +++ b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp @@ -82,6 +82,7 @@ #include "transformations/fp16_compression/mark_floatpoint_range.hpp" #include "transformations/init_node_info.hpp" #include "transformations/op_conversions/convert_avgpool_downgrade.hpp" +#include "transformations/mlir/convert.hpp" #include "transformations/op_conversions/convert_batch_to_space.hpp" #include "transformations/op_conversions/convert_broadcast3.hpp" #include "transformations/op_conversions/convert_broadcast_to_tiles.hpp" @@ -907,6 +908,8 @@ void Transformations::PreLpt(const std::vector& defaultPrecis CPU_REGISTER_PASS_COMMON(manager, ov::pass::ConstantFolding); CPU_REGISTER_PASS_COMMON(manager, ov::pass::LoraSubgraphFusion); + ov::pass::transformMLIR(model); + manager.run_passes(model); } From f6555b9eb59fb14a32b1371038f6561e8e90c2d0 Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Fri, 28 Jun 2024 17:18:16 +0200 Subject: [PATCH 002/121] Eliminate temporary buffer in binary op conversion (#130) Improves MLIR pipeline to avoid temporary buffer allocation and copy in linalg named binary operation conversion. Changes: - use tensor.empty in outs - add bufferization pre- and post-processing - mark outputs as 'restrict' --- .../src/transformations/mlir/convert.cpp | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index 220a6dfdc37b33..70fe5574f4b90b 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -70,7 +70,15 @@ static void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& m #else // Simplified default lowering to LLVM from LLVM tests + // Remove empty tensors to avoid converting them into temporary buffers. + pm.addPass(bufferization::createEmptyTensorEliminationPass()); + pm.addPass(bufferization::createOneShotBufferizePass()); + // TODO: Add deallocation pass/pipeline to avoid memory leaks. + + // Cleanup after bufferization - possibly remove redundant copies. + pm.addNestedPass(createCanonicalizerPass()); + pm.addNestedPass(createCSEPass()); // Blanket-convert any remaining high-level vector ops to loops if any remain. pm.addNestedPass(createConvertVectorToSCFPass()); @@ -553,11 +561,22 @@ template struct ConvertBinary { void operator()(ConversionContext& context, std::shared_ptr node) { auto loc = createLocation(context.context, node); + auto& builder = context.builder(); // TODO: Support broadcasts const auto inputs = context.getInputs(node); - auto op = context.builder().create(loc, - mlir::ValueRange{inputs[0], inputs[1]}, - /* FIXME: Use linalg.fill or tensor.empty */ mlir::ValueRange{inputs[0]}); + auto outType = cast(inputs[0].getType()); + // Named binary ops directly overwrite data in `outs` buffer so, there is no need to provide non-empty + // destination at the tensor-level. + // Use `tensor.empty` to avoid temporary buffer allocation and memcpy after bufferization. + llvm::SmallVector dynamicSizes; + for (auto [idx, dim] : llvm::enumerate(outType.getShape())) { + if (!mlir::ShapedType::isDynamic(dim)) + continue; + auto dimSize = builder.create(loc, inputs[0], idx); + dynamicSizes.push_back(dimSize); + } + auto empty = builder.create(loc, outType, dynamicSizes); + auto op = builder.create(loc, mlir::ValueRange{inputs[0], inputs[1]}, mlir::ValueRange{empty}); context.addOutputs(node, op); } }; @@ -606,8 +625,15 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, auto tensor = conversion_context.nodeOutputMap.at(outputs[i]); auto memref = func.getArgument(i + inputs.size()); auto loc = createLocation(context, outputs[i].get_node_shared_ptr()); - auto materialize = block_builder.create(loc, tensor, memref); - materialize.setWritable(true); // TODO: Can I set it as ctor argument above? + // Ensure the result is stored in the provided function argument. + // Mark as restrict to avoid temporary buffer and copy. + // Mark as writable to ensure the output can be written to the buffer. + block_builder.create(loc, + TypeRange{}, + tensor, + memref, + /*restrict=*/true, + /*writable=*/true); } const auto retLoc = createLayerLocation(context, "output", "Output"); From 2d1955b406e907556c48ad961040a1ae102c415f Mon Sep 17 00:00:00 2001 From: Renato Golin Date: Thu, 4 Jul 2024 16:40:17 +0100 Subject: [PATCH 003/121] Assert doesn't get compiled in Release mode (#131) This creates a scenario where the Expected doesn't get checked and the execution crashes, even if the engine was created correctly. --- .../transformations/src/transformations/mlir/convert.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index 70fe5574f4b90b..d63c4830f0b3eb 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -257,8 +257,12 @@ class MLIREvaluate { // in `create` independently engineOptions.llvmModuleBuilder = lowerToLLVMIR; auto maybeEngine = mlir::ExecutionEngine::create(module.get(), engineOptions); - assert(maybeEngine && "failed to construct an execution engine"); - engine = std::move(maybeEngine.get()); + if (maybeEngine) { + engine = std::move(maybeEngine.get()); + } else { + llvm::errs() << "failed to construct an execution engine\n"; + abort(); + } } bool invoke_packed(std::vector& args) { From 6f554c9e5c0a8e645d63e14dff644673867901cc Mon Sep 17 00:00:00 2001 From: Renato Golin Date: Mon, 8 Jul 2024 10:19:38 +0100 Subject: [PATCH 004/121] Reduce warnings (#133) Mostly static declarations, but one unnecessarily wide lambda capture. --- .../src/transformations/mlir/convert.cpp | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index d63c4830f0b3eb..c2d0984a33e182 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -121,7 +121,7 @@ static void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& m } } -std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext& llvmContext) { +static std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext& llvmContext) { // Default lowering for mlir-cpu-runner auto llvmModule = translateModuleToLLVMIR(module, llvmContext); assert(llvmModule); @@ -329,7 +329,7 @@ class OPENVINO_API MLIROp : public ov::op::Op { } }; -mlir::Location createLayerLocation(mlir::MLIRContext* ctx, const std::string& layerName, const std::string& layerType) { +static mlir::Location createLayerLocation(mlir::MLIRContext* ctx, const std::string& layerName, const std::string& layerType) { const auto layerNameAttr = mlir::StringAttr::get(ctx, layerName); const auto nameLoc = mlir::NameLoc::get(layerNameAttr); @@ -341,7 +341,7 @@ mlir::Location createLayerLocation(mlir::MLIRContext* ctx, const std::string& la return mlir::FusedLoc::get(ctx, {nameLoc}, metadata); } -SmallVector importShape(const ov::PartialShape& shape) { +static SmallVector importShape(const ov::PartialShape& shape) { SmallVector out(shape.rank().get_length()); // TODO: Add support for dynamically ranked shapes for (size_t i = 0; i < out.size(); ++i) { @@ -351,76 +351,76 @@ SmallVector importShape(const ov::PartialShape& shape) { return out; } -mlir::IntegerType getInt1Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getInt1Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 1); } -mlir::IntegerType getInt4Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getInt4Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 4); } -mlir::IntegerType getInt8Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getInt8Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 8); } -mlir::IntegerType getInt16Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getInt16Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 16); } -mlir::IntegerType getInt32Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getInt32Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 32); } -mlir::IntegerType getInt64Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getInt64Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 64); } -mlir::IntegerType getSInt4Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getSInt4Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 4, mlir::IntegerType::Signed); } -mlir::IntegerType getSInt8Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getSInt8Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Signed); } -mlir::IntegerType getSInt16Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getSInt16Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 16, mlir::IntegerType::Signed); } -mlir::IntegerType getSInt32Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getSInt32Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 32, mlir::IntegerType::Signed); } -mlir::IntegerType getSInt64Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getSInt64Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 64, mlir::IntegerType::Signed); } -mlir::IntegerType getUInt4Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getUInt4Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 4, mlir::IntegerType::Unsigned); } -mlir::IntegerType getUInt8Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getUInt8Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Unsigned); } -mlir::IntegerType getUInt16Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getUInt16Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 16, mlir::IntegerType::Unsigned); } -mlir::IntegerType getUInt32Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getUInt32Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 32, mlir::IntegerType::Unsigned); } -mlir::IntegerType getUInt64Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getUInt64Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 64, mlir::IntegerType::Unsigned); } -mlir::IntegerType getBool8Type(mlir::MLIRContext* ctx) { +static mlir::IntegerType getBool8Type(mlir::MLIRContext* ctx) { // Signless 8-bit integer use for BOOL, to distinguish it from U8 return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Signless); } -mlir::Type importPrecision(mlir::MLIRContext* ctx, const ov::element::Type& precision) { +static mlir::Type importPrecision(mlir::MLIRContext* ctx, const ov::element::Type& precision) { switch (precision) { case ov::element::Type_t::f64: return mlir::Float64Type::get(ctx); @@ -457,13 +457,13 @@ mlir::Type importPrecision(mlir::MLIRContext* ctx, const ov::element::Type& prec } } -mlir::RankedTensorType importTensor(mlir::MLIRContext* ctx, +static mlir::RankedTensorType importTensor(mlir::MLIRContext* ctx, const ov::PartialShape& shape, const ov::element::Type& elemType) { return mlir::RankedTensorType::get(ArrayRef(importShape(shape)), importPrecision(ctx, elemType)); } -mlir::Location createLocation(mlir::MLIRContext* ctx, std::shared_ptr node) { +static mlir::Location createLocation(mlir::MLIRContext* ctx, std::shared_ptr node) { return createLayerLocation(ctx, node->get_friendly_name(), node->get_type_name()); } @@ -488,14 +488,14 @@ struct hash> final { } // namespace std -MemRefType convertTensorToMemRef(TensorType tensorType) { +static MemRefType convertTensorToMemRef(TensorType tensorType) { ArrayRef shape = tensorType.getShape(); Type elementType = tensorType.getElementType(); return MemRefType::get(shape, elementType); } -SmallVector tensorsToMemRefs(SmallVector tensors) { +static SmallVector tensorsToMemRefs(SmallVector tensors) { SmallVector out; out.reserve(tensors.size()); for (const auto& tensor : tensors) { @@ -505,7 +505,7 @@ SmallVector tensorsToMemRefs(SmallVector tensors) { } -SmallVector get_types_for_values(mlir::MLIRContext* context, const ov::OutputVector& values) { +static SmallVector get_types_for_values(mlir::MLIRContext* context, const ov::OutputVector& values) { SmallVector types; types.reserve(values.size()); for (const auto& output : values) { @@ -588,7 +588,7 @@ struct ConvertBinary { const std::map ConversionContext::convertors = { {ov::op::v1::Add::get_type_info_static(), Convertor(ConvertBinary())}}; -mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, +static mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, const ov::OutputVector& inputs, const ov::NodeVector& nodes, const ov::OutputVector& outputs) { @@ -653,7 +653,7 @@ class AddLowering : public ov::pass::MatcherPass { auto pattern = ov::pass::pattern::wrap_type( {ov::pass::pattern::any_input(), ov::pass::pattern::any_input()}); - auto callback = [=, context](ov::pass::pattern::Matcher& m) { + auto callback = [context](ov::pass::pattern::Matcher& m) { std::cout << "[ INFO ] Matched AddLowering\n"; auto add = m.get_match_root(); @@ -682,7 +682,7 @@ class AddLowering : public ov::pass::MatcherPass { }; -void injectMLIR(std::shared_ptr model, MLIRContext* context) { +static void injectMLIR(std::shared_ptr model, MLIRContext* context) { ov::pass::Manager manager; manager.set_per_pass_validation(true); manager.register_pass(context); @@ -690,7 +690,7 @@ void injectMLIR(std::shared_ptr model, MLIRContext* context) { } -MLIRContext* get_shared_mlir_context() { +static MLIRContext* get_shared_mlir_context() { // Gives MLIRContext instance shared for entire OV process and initialized once upon the initial request // FIXME: Bind with OpenVINO lifetime in the sutable class instead of dirty tricking with static lifetime From 560a659f08250eb1652e6307c400c4480606d5bc Mon Sep 17 00:00:00 2001 From: Sergey Lyalin Date: Tue, 9 Jul 2024 14:45:39 +0400 Subject: [PATCH 005/121] Multi-node patterns with Add, Sub, Mul, Div (#135) * Simple graph patritioner * Multi-node MLIR lowering in a correct partition termination, explicit conversion of not broadcastale nodes: Add, Sub, Mul, Div. * Minor: moved elementwise_f32_etc to outer scope, removed debug output * static -> namespace {} --- .../src/transformations/mlir/convert.cpp | 432 +++++++++++++++--- 1 file changed, 369 insertions(+), 63 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index c2d0984a33e182..a76feabfd9cf71 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -7,6 +7,10 @@ #include #include #include +#include +#include +#include + #include #include #include @@ -55,11 +59,20 @@ #include "openvino/core/rt_info.hpp" #include "openvino/pass/pattern/op/wrap_type.hpp" #include "transformations_visibility.hpp" +#include "openvino/core/symbol.hpp" + +#include "transformations/symbolic_transformations/symbolic_optimizations.hpp" + +namespace { using namespace mlir; +using NodePtr = std::shared_ptr; +using SymbolPtr = std::shared_ptr; + -static void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module) { + +void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module) { // A set of default passes that lower any input IR to LLVM PassManager pm(module->getContext()); @@ -121,7 +134,7 @@ static void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& m } } -static std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext& llvmContext) { +std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext& llvmContext) { // Default lowering for mlir-cpu-runner auto llvmModule = translateModuleToLLVMIR(module, llvmContext); assert(llvmModule); @@ -277,7 +290,7 @@ class MLIREvaluate { typedef std::vector> OVOutputTypes; -class OPENVINO_API MLIROp : public ov::op::Op { +class MLIROp : public ov::op::Op { std::shared_ptr engine; OVOutputTypes output_types; @@ -298,13 +311,11 @@ class OPENVINO_API MLIROp : public ov::op::Op { } } - std::shared_ptr clone_with_new_inputs(const ov::OutputVector& new_args) const override { + NodePtr clone_with_new_inputs(const ov::OutputVector& new_args) const override { return std::make_shared(new_args, engine, output_types); } bool evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const override { - // FIXME: Assumes an output contains only one tensor which shape is just propagated from inputs[0]. - // TODO: Involve internal for a partition shape propagation or rely on symbolic shape info. outputs[0].set_shape(inputs[0].get_shape()); std::vector memref_args; @@ -329,7 +340,7 @@ class OPENVINO_API MLIROp : public ov::op::Op { } }; -static mlir::Location createLayerLocation(mlir::MLIRContext* ctx, const std::string& layerName, const std::string& layerType) { +mlir::Location createLayerLocation(mlir::MLIRContext* ctx, const std::string& layerName, const std::string& layerType) { const auto layerNameAttr = mlir::StringAttr::get(ctx, layerName); const auto nameLoc = mlir::NameLoc::get(layerNameAttr); @@ -341,7 +352,7 @@ static mlir::Location createLayerLocation(mlir::MLIRContext* ctx, const std::str return mlir::FusedLoc::get(ctx, {nameLoc}, metadata); } -static SmallVector importShape(const ov::PartialShape& shape) { +SmallVector importShape(const ov::PartialShape& shape) { SmallVector out(shape.rank().get_length()); // TODO: Add support for dynamically ranked shapes for (size_t i = 0; i < out.size(); ++i) { @@ -351,76 +362,76 @@ static SmallVector importShape(const ov::PartialShape& shape) { return out; } -static mlir::IntegerType getInt1Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getInt1Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 1); } -static mlir::IntegerType getInt4Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getInt4Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 4); } -static mlir::IntegerType getInt8Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getInt8Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 8); } -static mlir::IntegerType getInt16Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getInt16Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 16); } -static mlir::IntegerType getInt32Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getInt32Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 32); } -static mlir::IntegerType getInt64Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getInt64Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 64); } -static mlir::IntegerType getSInt4Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getSInt4Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 4, mlir::IntegerType::Signed); } -static mlir::IntegerType getSInt8Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getSInt8Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Signed); } -static mlir::IntegerType getSInt16Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getSInt16Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 16, mlir::IntegerType::Signed); } -static mlir::IntegerType getSInt32Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getSInt32Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 32, mlir::IntegerType::Signed); } -static mlir::IntegerType getSInt64Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getSInt64Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 64, mlir::IntegerType::Signed); } -static mlir::IntegerType getUInt4Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getUInt4Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 4, mlir::IntegerType::Unsigned); } -static mlir::IntegerType getUInt8Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getUInt8Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Unsigned); } -static mlir::IntegerType getUInt16Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getUInt16Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 16, mlir::IntegerType::Unsigned); } -static mlir::IntegerType getUInt32Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getUInt32Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 32, mlir::IntegerType::Unsigned); } -static mlir::IntegerType getUInt64Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getUInt64Type(mlir::MLIRContext* ctx) { return mlir::IntegerType::get(ctx, 64, mlir::IntegerType::Unsigned); } -static mlir::IntegerType getBool8Type(mlir::MLIRContext* ctx) { +mlir::IntegerType getBool8Type(mlir::MLIRContext* ctx) { // Signless 8-bit integer use for BOOL, to distinguish it from U8 return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Signless); } -static mlir::Type importPrecision(mlir::MLIRContext* ctx, const ov::element::Type& precision) { +mlir::Type importPrecision(mlir::MLIRContext* ctx, const ov::element::Type& precision) { switch (precision) { case ov::element::Type_t::f64: return mlir::Float64Type::get(ctx); @@ -457,16 +468,19 @@ static mlir::Type importPrecision(mlir::MLIRContext* ctx, const ov::element::Typ } } -static mlir::RankedTensorType importTensor(mlir::MLIRContext* ctx, +mlir::RankedTensorType importTensor(mlir::MLIRContext* ctx, const ov::PartialShape& shape, const ov::element::Type& elemType) { return mlir::RankedTensorType::get(ArrayRef(importShape(shape)), importPrecision(ctx, elemType)); } -static mlir::Location createLocation(mlir::MLIRContext* ctx, std::shared_ptr node) { +mlir::Location createLocation(mlir::MLIRContext* ctx, NodePtr node) { return createLayerLocation(ctx, node->get_friendly_name(), node->get_type_name()); } +} // namespace + + namespace std { template @@ -488,14 +502,16 @@ struct hash> final { } // namespace std -static MemRefType convertTensorToMemRef(TensorType tensorType) { +namespace { + +MemRefType convertTensorToMemRef(TensorType tensorType) { ArrayRef shape = tensorType.getShape(); Type elementType = tensorType.getElementType(); return MemRefType::get(shape, elementType); } -static SmallVector tensorsToMemRefs(SmallVector tensors) { +SmallVector tensorsToMemRefs(SmallVector tensors) { SmallVector out; out.reserve(tensors.size()); for (const auto& tensor : tensors) { @@ -505,7 +521,7 @@ static SmallVector tensorsToMemRefs(SmallVector tensors) } -static SmallVector get_types_for_values(mlir::MLIRContext* context, const ov::OutputVector& values) { +SmallVector get_types_for_values(mlir::MLIRContext* context, const ov::OutputVector& values) { SmallVector types; types.reserve(values.size()); for (const auto& output : values) { @@ -516,7 +532,7 @@ static SmallVector get_types_for_values(mlir::MLIRContext* context, class ConversionContext { public: - using Convertor = std::function)>; + using Convertor = std::function; using NodeOutputMap = std::unordered_map, mlir::Value>; static const std::map convertors; @@ -528,7 +544,7 @@ class ConversionContext { : context(context), block_builder(block_builder) {} - SmallVector getInputs(std::shared_ptr node) { + SmallVector getInputs(NodePtr node) { SmallVector out; out.reserve(node->get_input_size()); for (const auto& input : node->inputs()) { @@ -537,7 +553,7 @@ class ConversionContext { return out; } - void addOutputs(std::shared_ptr node, mlir::Operation* op) { + void addOutputs(NodePtr node, mlir::Operation* op) { const auto results = op->getOpResults(); OPENVINO_ASSERT( @@ -556,14 +572,14 @@ class ConversionContext { return *block_builder; } - void convert(std::shared_ptr node) { + void convert(NodePtr node) { convertors.at(node->get_type_info())(*this, node); } }; template struct ConvertBinary { - void operator()(ConversionContext& context, std::shared_ptr node) { + void operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); // TODO: Support broadcasts @@ -586,9 +602,13 @@ struct ConvertBinary { }; const std::map ConversionContext::convertors = { - {ov::op::v1::Add::get_type_info_static(), Convertor(ConvertBinary())}}; + {ov::op::v1::Add::get_type_info_static(), Convertor(ConvertBinary())}, + {ov::op::v1::Subtract::get_type_info_static(), Convertor(ConvertBinary())}, + {ov::op::v1::Multiply::get_type_info_static(), Convertor(ConvertBinary())}, + {ov::op::v1::Divide::get_type_info_static(), Convertor(ConvertBinary())}, +}; -static mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, +mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, const ov::OutputVector& inputs, const ov::NodeVector& nodes, const ov::OutputVector& outputs) { @@ -646,51 +666,335 @@ static mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, return module; } -class AddLowering : public ov::pass::MatcherPass { + +using InputVector = std::vector>; + + +struct Subgraph { + ov::NodeVector nodes; + ov::OutputVector inputs; + ov::OutputVector outputs; + std::vector output_consumers; + + // Consumes other subgraph + void merge (Subgraph& other) { + nodes.insert(nodes.end(), other.nodes.begin(), other.nodes.end()); + } +}; + + +using SubgraphPtr = std::shared_ptr; +using SubgraphID = SymbolPtr; + + +class SubgraphTracker { public: - OPENVINO_RTTI("AddLowering", "0"); - explicit AddLowering(mlir::MLIRContext* context) { - auto pattern = ov::pass::pattern::wrap_type( - {ov::pass::pattern::any_input(), ov::pass::pattern::any_input()}); - auto callback = [context](ov::pass::pattern::Matcher& m) { - std::cout << "[ INFO ] Matched AddLowering\n"; - auto add = m.get_match_root(); + // callback to finalize the subgraph when it is terminated + using Finalizer = std::function; - mlir::OwningOpRef module; + SubgraphTracker(Finalizer finalizer): m_finalizer(finalizer) {} - // FIXME: Suppose no broadcast - module = ngraph_to_mlir(context, add->input_values(), {add}, add->outputs()); + void add_node (NodePtr node, bool belongs) { + // collect all subgraph ids that input nodes belong to and all dependencies + Dependencies input_subgraphs; + Dependencies input_dependencies; + for(auto input_value: node->input_values()) { + auto node = input_value.get_node_shared_ptr(); + if(auto id = get_subgraph_id(node)) { + input_subgraphs.insert(ov::symbol::ancestor_of(id)); + } + const auto& deps = get_dependencies(node); + for(auto dep: deps) { + input_dependencies.insert(ov::symbol::ancestor_of(dep)); + } + } - auto expected_outputs = add->outputs(); - OVOutputTypes output_types; - for (size_t i = 0; i < expected_outputs.size(); ++i) { - output_types.push_back( - std::make_tuple(expected_outputs[i].get_element_type(), expected_outputs[i].get_partial_shape())); + if(belongs) { + // Below we refuse to merge subgraphs if all of them cannot merge to a single subgraph, this is rough because + // there are cases when part of the input subgraphs can consume the node and others will come as inputs -- TODO. + // TODO: leave only those input subgraphs that are not conflicting with other subgraphs nor with any dependencies + if(input_subgraphs.empty() || intersected(input_subgraphs, input_dependencies)) { // no input subgraphs || cannot merge all due to cycles + try_terminate_subgraphs(input_subgraphs, node); + + // start a new subgraph + auto subgraph_id = new_subgraph(); + add_node_to_subgraph(node, subgraph_id); + set_subgraph_id(node, subgraph_id); + input_dependencies.insert(input_subgraphs.begin(), input_subgraphs.end()); + } else { + auto merged_subgraph_id = std::accumulate( + input_subgraphs.begin(), + input_subgraphs.end(), + *input_subgraphs.begin(), + [this](SubgraphID a, SubgraphID b) { + merge_subgraphs(a, b); + return a; + } + ); + set_subgraph_id(node, merged_subgraph_id); + add_node_to_subgraph(node, merged_subgraph_id); } - auto replacement = std::make_shared(add->input_values(), - std::make_shared(std::move(module)), - output_types); - replace_node(add, replacement); + } else { + try_terminate_subgraphs(input_subgraphs, node); + set_subgraph_id(node, nullptr); + input_dependencies.insert(input_subgraphs.begin(), input_subgraphs.end()); + } + set_dependencies(node, input_dependencies); + } + + void finalize() { + for(auto subgraph_record: m_subgraphs) { + terminate_subgraph(subgraph_record.first); + } + } + +private: + + std::unordered_map m_subgraphs; + using Dependencies = std::unordered_set; + Finalizer m_finalizer; + + // // Detects if `node` depends on a node from `subgraph` but goes via node that doesn't belongs to `subgraph` + // bool depends_via_break (NodePtr node, const Subgraph& subgraph); + + SubgraphID new_subgraph() { + SubgraphID id = std::make_shared(); + m_subgraphs[id] = std::make_shared(); + return id; + } + + void add_node_to_subgraph(NodePtr node, SubgraphID id) { + get_subgraph(id)->nodes.push_back(node); + } + + void merge_subgraphs(SubgraphID id1, SubgraphID id2) { + id1 = ov::symbol::ancestor_of(id1); + id2 = ov::symbol::ancestor_of(id2); + if (id1 == id2) return; + + auto subgraph1 = get_subgraph(id1); + auto subgraph2 = get_subgraph(id2); + subgraph1->merge(*subgraph2); + m_subgraphs.erase(id1); + m_subgraphs.erase(id2); + ov::symbol::set_equal(id1, id2); + id1 = ov::symbol::ancestor_of(id1); + m_subgraphs[id1] = subgraph1; + } + + SubgraphPtr get_subgraph(SubgraphID id) { + return m_subgraphs.at(ov::symbol::ancestor_of(id)); + } + + // set/get all subgraph ids that contribute to a given node + + const Dependencies& get_dependencies(NodePtr node) { + return node->get_rt_info().at("__subgraph_dependencies").as(); + } + void set_dependencies(NodePtr node, const Dependencies& dependencies) { + node->get_rt_info()["__subgraph_dependencies"] = dependencies; + } + + // set/get subgraph id that a give node belongs to + + SubgraphID get_subgraph_id(NodePtr node) { + auto id = node->get_rt_info().at("__subgraph_id").as(); + if(id) { + id = ov::symbol::ancestor_of(id); + } + return id; + } + + void set_subgraph_id(NodePtr node, SubgraphID id) { + node->get_rt_info()["__subgraph_id"] = id; + } + + bool intersected(const Dependencies& a, const Dependencies& b) { + for(const auto& x: a) { + if(b.count(x)) + return true; + } + return false; + } + + void terminate_subgraph(SubgraphID id) { + id = ov::symbol::ancestor_of(id); + auto subgraph = get_subgraph(id); + // Build subgraph inputs and outputs + std::unordered_set> inputs; + auto& outputs = subgraph->outputs; + auto& output_consumers = subgraph->output_consumers; + for(auto node: subgraph->nodes) { + for(auto input: node->input_values()) { + auto input_id = get_subgraph_id(input.get_node_shared_ptr()); + if(!ov::symbol::are_equal(id, input_id)) { + inputs.insert(input); + } + } + for(auto output: node->outputs()) { + const auto& consumers = output.get_target_inputs(); + InputVector external_consumers; + for(auto consumer: consumers) { + auto consumer_id = get_subgraph_id(consumer.get_node()->shared_from_this()); + if(!ov::symbol::are_equal(id, consumer_id)) { + external_consumers.push_back(consumer); + } + } + bool used_outside = !external_consumers.empty(); + if(used_outside) { + outputs.push_back(output); + output_consumers.push_back(external_consumers); + } + } + } + subgraph->inputs.assign(inputs.begin(), inputs.end()); + m_finalizer(subgraph); + } + + void try_terminate_subgraphs(const Dependencies& subgraphs, NodePtr terminator) { + // TODO: Terminate subgraphs earlier when all terminating nodes are known + // TODO: try to merge subgraphs if they are being terminated simultaniously + } +}; + +// This pass find marked with a special flag group of nodes and collapse each group to a single MLIR function +NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph) { + + mlir::OwningOpRef module; + + module = ngraph_to_mlir(context, subgraph->inputs, subgraph->nodes, subgraph->outputs); + + OVOutputTypes output_types; + for (size_t i = 0; i < subgraph->outputs.size(); ++i) { + output_types.push_back( + std::make_tuple(subgraph->outputs[i].get_element_type(), subgraph->outputs[i].get_partial_shape())); + } + return std::make_shared( + subgraph->inputs, + std::make_shared(std::move(module)), + output_types + ); +}; + + +const std::string& subgraph_mark() { + static const std::string mark = "__subgraph_mlir_mark"; + return mark; +} + +void set_subgraph_mark(NodePtr node) { + node->get_rt_info()[subgraph_mark()]; +} + +bool get_subgraph_mark(NodePtr node) { + return node->get_rt_info().count(subgraph_mark()); +} + +class MarkPattern : public ov::pass::MatcherPass { +public: + OPENVINO_RTTI("MarkPattern", "0"); + MarkPattern(NodePtr pattern) { + auto callback = [](ov::pass::pattern::Matcher& m) { + // TODO: support multi-node patterns marking + auto node = m.get_match_root(); + set_subgraph_mark(node); return true; }; - auto m = std::make_shared(pattern, "AddLowering"); + auto m = std::make_shared(pattern, "MarkPattern"); register_matcher(m, callback); } }; -static void injectMLIR(std::shared_ptr model, MLIRContext* context) { +void replace_subgraph(SubgraphPtr subgraph, NodePtr node) { + const auto& output_consumers = subgraph->output_consumers; + assert(output_consumers.size() == node->get_output_size()); + for(size_t i = 0; i < node->get_output_size(); ++i) { + auto replacement = node->output(i); + for(auto consumer: output_consumers[i]) { + consumer.replace_source_output(replacement); + } + } +} + + +class Partitioner : public ov::pass::ModelPass { + MLIRContext* context; +public: + OPENVINO_RTTI("Partitioner"); + + Partitioner(MLIRContext* context) : context(context) {} + + bool run_on_model(const std::shared_ptr& model) override { + SubgraphTracker tracker([this](SubgraphPtr subgraph) { + auto mlir_op = ngraph_to_mlir_op(context, subgraph); + replace_subgraph(subgraph, mlir_op); + std::cerr << "Created MLIR op: " << mlir_op << "\n"; + } + ); + for(auto node: model->get_ordered_ops()) { + tracker.add_node(node, get_subgraph_mark(node)); + } + tracker.finalize(); + } +}; + + +bool elementwise_f32_binary_no_broadcast_predicate(const ov::Output& output) { + if(output.get_element_type() != ov::element::f32) { + return false; + } + // Check if implicit broadcast is possible, reject in this case + // Relies on symbolic information -- register SymbolicPropagation before applying this pattern + auto input_shape_a = output.get_node_shared_ptr()->get_input_partial_shape(0); + auto input_shape_b = output.get_node_shared_ptr()->get_input_partial_shape(1); + auto output_shape = output.get_partial_shape(); + if(output_shape.rank().is_dynamic() || input_shape_a.rank().is_dynamic() || input_shape_b.rank().is_dynamic()) { + return false; + } + if(output_shape.rank().get_length() != input_shape_a.rank().get_length() || output_shape.rank().get_length() != input_shape_b.rank().get_length()) { + return false; + } + + for(size_t i = 0; i < output_shape.size(); ++i) { + if(output_shape[i] != input_shape_a[i] || output_shape[i] != input_shape_b[i]) { + return false; + } + if(!ov::symbol::are_equal(output_shape[i].get_symbol(), input_shape_a[i].get_symbol()) || !ov::symbol::are_equal(output_shape[i].get_symbol(), input_shape_b[i].get_symbol())) { + return false; + } + } + + return true; +} + + +template +NodePtr elementwise_f32_binary_no_broadcast() { + using namespace ov::pass::pattern; + return wrap_type({any_input(), any_input()}, elementwise_f32_binary_no_broadcast_predicate); +} + + +void injectMLIR(std::shared_ptr model, MLIRContext* context) { ov::pass::Manager manager; - manager.set_per_pass_validation(true); - manager.register_pass(context); + using namespace ov::op; + manager.set_per_pass_validation(false); + manager.register_pass(); + manager.register_pass(elementwise_f32_binary_no_broadcast()); + manager.register_pass(elementwise_f32_binary_no_broadcast()); + manager.register_pass(elementwise_f32_binary_no_broadcast()); + manager.register_pass(elementwise_f32_binary_no_broadcast()); + manager.register_pass(context); manager.run_passes(model); + model->validate_nodes_and_infer_types(); } -static MLIRContext* get_shared_mlir_context() { +MLIRContext* get_shared_mlir_context() { // Gives MLIRContext instance shared for entire OV process and initialized once upon the initial request // FIXME: Bind with OpenVINO lifetime in the sutable class instead of dirty tricking with static lifetime @@ -729,6 +1033,8 @@ static MLIRContext* get_shared_mlir_context() { return context.get(); } +} // namespace + void ov::pass::transformMLIR(std::shared_ptr model) { injectMLIR(model, get_shared_mlir_context()); } From 08e601dc1819d79a1b923d877bc7ca2a35bfb8ea Mon Sep 17 00:00:00 2001 From: Sergey Lyalin Date: Wed, 10 Jul 2024 13:40:11 +0400 Subject: [PATCH 006/121] Reorg: split monolithic convert.cpp into multiple files (#137) * Moved MLIROp, transformation pipeline and evaluation tools to a separate files mlir_op.hpp/cpp * Moved common conversion for precision nad shape to a separate file * Move SubgraphTracker to a separate file. Not use unordered_ containers and delete hash function for Output. Cleanup. --- .../src/transformations/mlir/convert.cpp | 639 +----------------- .../transformations/mlir/convert_common.cpp | 132 ++++ .../transformations/mlir/convert_common.hpp | 33 + .../src/transformations/mlir/mlir_op.cpp | 323 +++++++++ .../src/transformations/mlir/mlir_op.hpp | 53 ++ .../transformations/mlir/subgraph_tracker.cpp | 179 +++++ .../transformations/mlir/subgraph_tracker.hpp | 66 ++ .../src/transformations/mlir/typedefs.hpp | 20 + 8 files changed, 814 insertions(+), 631 deletions(-) create mode 100644 src/common/transformations/src/transformations/mlir/convert_common.cpp create mode 100644 src/common/transformations/src/transformations/mlir/convert_common.hpp create mode 100644 src/common/transformations/src/transformations/mlir/mlir_op.cpp create mode 100644 src/common/transformations/src/transformations/mlir/mlir_op.hpp create mode 100644 src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp create mode 100644 src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp create mode 100644 src/common/transformations/src/transformations/mlir/typedefs.hpp diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index a76feabfd9cf71..dc2cb8d1910ef7 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -16,7 +16,7 @@ #include #include -#include "itt.hpp" +// TODO: Prune unused headers -- it's hard to understand needed ones #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Casting.h" #include "llvm/Support/InitLLVM.h" @@ -63,447 +63,16 @@ #include "transformations/symbolic_transformations/symbolic_optimizations.hpp" -namespace { - -using namespace mlir; - -using NodePtr = std::shared_ptr; -using SymbolPtr = std::shared_ptr; - - - -void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module) { - // A set of default passes that lower any input IR to LLVM - PassManager pm(module->getContext()); - -#if 0 // TODO: if TPP is available - - tpp::DefaultPipelineOptions defPipelineOpts{defGpuBackend}; - pm.addPass(tpp::createDefaultPipeline(defPipelineOpts)); - -#else // Simplified default lowering to LLVM from LLVM tests - - // Remove empty tensors to avoid converting them into temporary buffers. - pm.addPass(bufferization::createEmptyTensorEliminationPass()); - - pm.addPass(bufferization::createOneShotBufferizePass()); - // TODO: Add deallocation pass/pipeline to avoid memory leaks. - - // Cleanup after bufferization - possibly remove redundant copies. - pm.addNestedPass(createCanonicalizerPass()); - pm.addNestedPass(createCSEPass()); - - // Blanket-convert any remaining high-level vector ops to loops if any remain. - pm.addNestedPass(createConvertVectorToSCFPass()); - // pm.addNestedPass(createLinalgGeneralizeNamedOpsPass()); - // Blanket-convert any remaining linalg ops to loops if any remain. - pm.addNestedPass(createConvertLinalgToLoopsPass()); - // Blanket-convert any remaining affine ops if any remain. - pm.addPass(createLowerAffinePass()); - // Convert SCF to CF (always needed). - pm.addPass(createConvertSCFToCFPass()); - // Sprinkle some cleanups. - pm.addPass(createCanonicalizerPass()); - pm.addPass(createCSEPass()); - // Blanket-convert any remaining linalg ops to LLVM if any remain. - // pm.addPass(createConvertLinalgToLLVMPass()); // no such pass - // Convert vector to LLVM (always needed). - pm.addPass(createConvertVectorToLLVMPass()); - // Convert Math to LLVM (always needed). - pm.addNestedPass(createConvertMathToLLVMPass()); - // Expand complicated MemRef operations before lowering them. - pm.addPass(memref::createExpandStridedMetadataPass()); - // The expansion may create affine expressions. Get rid of them. - pm.addPass(createLowerAffinePass()); - // Convert MemRef to LLVM (always needed). - // pm.addPass(memref::createExpandOpsPass()); - pm.addPass(createFinalizeMemRefToLLVMConversionPass()); - // Convert Func to LLVM (always needed). - pm.addPass(createConvertFuncToLLVMPass()); - // Convert Index to LLVM (always needed). - pm.addPass(createConvertIndexToLLVMPass()); - // Convert remaining unrealized_casts (always needed). - pm.addPass(createReconcileUnrealizedCastsPass()); - -#endif - - auto result = pm.run(module.get()); - if (failed(result)) { - llvm::errs() << "ERROR: Failed to lower IR to LLVM dialect\n"; - module->print(llvm::errs()); - } -} - -std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext& llvmContext) { - // Default lowering for mlir-cpu-runner - auto llvmModule = translateModuleToLLVMIR(module, llvmContext); - assert(llvmModule); - - // Target machine, null if not specified - std::unique_ptr targetMachine; - - std::string triple = "x86_64-linux-gnu"; - std::string cpuName = "alderlake"; // sapphirerapids, nehalem, etc. - std::string fpuName = "avx2"; // sse4.2, avx, avx2, avx512bf16, etc. - bool printLLVM = false; - auto codeGenOpt = 2; - - // Specify target machine - if (!triple.empty() && !cpuName.empty()) { - std::string error; - const llvm::Target* target = llvm::TargetRegistry::lookupTarget(triple, error); - if (!target) { - llvm::errs() << "Error while looking up target triple: "; - llvm::errs() << error << "\n"; - return nullptr; - } - - // These options should force fused MLA, but they don't. :/ - // Adding unsafe math attribute to functions below do the trick. - llvm::TargetOptions targetOptions; - targetOptions.UnsafeFPMath = true; - targetOptions.AllowFPOpFusion = llvm::FPOpFusion::FPOpFusionMode::Fast; - targetMachine.reset(target->createTargetMachine(triple, - cpuName, - "+" + fpuName, - targetOptions, - /* reloc model */ std::nullopt, - /* code model */ std::nullopt, - llvm::CodeGenOptLevel(codeGenOpt))); - if (!targetMachine) { - llvm::errs() << "Error while looking up target CPU: "; - llvm::errs() << cpuName << "\n"; - return nullptr; - } - } - - // Run the optimized pipeline - int sizeLevel = 0; - auto optPipeline = makeOptimizingTransformer(codeGenOpt, sizeLevel, targetMachine.get()); - if (auto err = optPipeline(llvmModule.get())) { - llvmModule->print(llvm::errs(), nullptr); - llvm::errs() << "Error while passing through the LLVM pipeline: "; - llvm::errs() << err << "\n"; - return nullptr; - } - - // MLIR doesn't lower LLVM with fast-math flags, but we need that, so we - // add for each function, to get FMAs and other goodies. - for (auto& func : llvmModule->functions()) { - func.addFnAttr("unsafe-fp-math", "true"); - } - - if (printLLVM) - llvmModule->print(llvm::outs(), nullptr); - - return llvmModule; -} - - -// TODO: u4/i4 types are not supported -struct MemRef { - MemRef() = default; - - MemRef(ov::Tensor tensor) - : allocated(tensor.data()), - aligned(tensor.data()), - offset(0), - shape(tensor.get_shape().begin(), tensor.get_shape().end()) { - strides.resize(tensor.get_shape().size()); - const auto& byte_strides = tensor.get_strides(); - auto element_size = tensor.get_element_type().size(); - for (size_t i = 0; i < strides.size(); ++i) { - assert(byte_strides[i] % element_size == 0); - // TODO: handle case when stride is not aligned (restrict at OV API level) - strides[i] = byte_strides[i] / element_size; - //std::cout << "stride [" << i << "] = " << strides[i] << "\n"; - } - } - - void* allocated; - void* aligned; - int64_t offset; - std::vector shape; - std::vector strides; - - void append_to_packed_args(std::vector& args) { - args.push_back(&allocated); - args.push_back(&aligned); - args.push_back(&offset); - for (size_t i = 0; i < shape.size(); ++i) { - args.push_back(&shape[i]); - } - for (size_t i = 0; i < strides.size(); ++i) { - args.push_back(&strides[i]); - } - } -}; - -class MLIREvaluate { - OwningOpRef module; // FIXME: needs to be kept? - std::unique_ptr engine; - -public: - MLIREvaluate(OwningOpRef _module) : module(std::move(_module)) { - if (true) { - std::cout << "[ DEBUG ] Source MLIR:\n"; - std::cerr << "-----------------------------------------\n"; - module->dump(); - std::cout << "-----------------------------------------\n"; - } - - prepareMLIRKernelWithoutWrapper(module); - - if (true) { - std::cerr << "[ DEBUG ] Target LLVM:\n"; - std::cerr << "-----------------------------------------\n"; - module->dump(); - std::cerr << "-----------------------------------------\n"; - } - - auto optPipeline = mlir::makeOptimizingTransformer(2, - /*sizeLevel=*/0, // FIXME: HARDCODED - /*targetMachine=*/nullptr); - - mlir::ExecutionEngineOptions engineOptions; - engineOptions.transformer = optPipeline; // opt level looks to be overriden in lowerToLLVMIR, but is still used - // in `create` independently - engineOptions.llvmModuleBuilder = lowerToLLVMIR; - auto maybeEngine = mlir::ExecutionEngine::create(module.get(), engineOptions); - if (maybeEngine) { - engine = std::move(maybeEngine.get()); - } else { - llvm::errs() << "failed to construct an execution engine\n"; - abort(); - } - } - - bool invoke_packed(std::vector& args) { - auto invocationResult = engine->invokePacked("entry", args); - if (invocationResult) { - llvm::errs() << "JIT invocation failed\n"; - return false; - } - return true; - } -}; - -typedef std::vector> OVOutputTypes; - -class MLIROp : public ov::op::Op { - std::shared_ptr engine; - OVOutputTypes output_types; - -public: - OPENVINO_OP("MLIROp"); - - MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types) - : Op(args), - engine(engine), - output_types(output_types) { - constructor_validate_and_infer_types(); - } - - void validate_and_infer_types() override { - set_output_size(output_types.size()); - for (size_t i = 0; i < output_types.size(); ++i) { - set_output_type(i, std::get<0>(output_types[i]), std::get<1>(output_types[i])); - } - } - - NodePtr clone_with_new_inputs(const ov::OutputVector& new_args) const override { - return std::make_shared(new_args, engine, output_types); - } - - bool evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const override { - outputs[0].set_shape(inputs[0].get_shape()); - - std::vector memref_args; - for (size_t i = 0; i < inputs.size(); ++i) { - memref_args.push_back(MemRef(inputs[i])); - } - for (size_t i = 0; i < outputs.size(); ++i) { - memref_args.push_back(MemRef(outputs[i])); - } - std::vector args; - - std::for_each(memref_args.begin(), memref_args.end(), [&args](MemRef& x) { - x.append_to_packed_args(args); - }); - - std::cerr << "[ INFO ] Running kernel in MLIROp::evaluate\n"; - return engine->invoke_packed(args); - } - - bool has_evaluate() const override { - return true; - } -}; - -mlir::Location createLayerLocation(mlir::MLIRContext* ctx, const std::string& layerName, const std::string& layerType) { - const auto layerNameAttr = mlir::StringAttr::get(ctx, layerName); - const auto nameLoc = mlir::NameLoc::get(layerNameAttr); - - SmallVector fields; - fields.emplace_back(mlir::StringAttr::get(ctx, "type"), mlir::StringAttr::get(ctx, layerType)); - fields.emplace_back(mlir::StringAttr::get(ctx, "name"), layerNameAttr); - auto metadata = mlir::DictionaryAttr::get(ctx, fields); - - return mlir::FusedLoc::get(ctx, {nameLoc}, metadata); -} - -SmallVector importShape(const ov::PartialShape& shape) { - SmallVector out(shape.rank().get_length()); - // TODO: Add support for dynamically ranked shapes - for (size_t i = 0; i < out.size(); ++i) { - const auto& dim = shape[i]; - out[i] = dim.is_static() ? dim.get_length() : mlir::ShapedType::kDynamic; - } - return out; -} - -mlir::IntegerType getInt1Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 1); -} - -mlir::IntegerType getInt4Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 4); -} - -mlir::IntegerType getInt8Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 8); -} - -mlir::IntegerType getInt16Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 16); -} - -mlir::IntegerType getInt32Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 32); -} - -mlir::IntegerType getInt64Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 64); -} - -mlir::IntegerType getSInt4Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 4, mlir::IntegerType::Signed); -} - -mlir::IntegerType getSInt8Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Signed); -} - -mlir::IntegerType getSInt16Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 16, mlir::IntegerType::Signed); -} - -mlir::IntegerType getSInt32Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 32, mlir::IntegerType::Signed); -} - -mlir::IntegerType getSInt64Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 64, mlir::IntegerType::Signed); -} - -mlir::IntegerType getUInt4Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 4, mlir::IntegerType::Unsigned); -} - -mlir::IntegerType getUInt8Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Unsigned); -} - -mlir::IntegerType getUInt16Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 16, mlir::IntegerType::Unsigned); -} - -mlir::IntegerType getUInt32Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 32, mlir::IntegerType::Unsigned); -} - -mlir::IntegerType getUInt64Type(mlir::MLIRContext* ctx) { - return mlir::IntegerType::get(ctx, 64, mlir::IntegerType::Unsigned); -} - -mlir::IntegerType getBool8Type(mlir::MLIRContext* ctx) { - // Signless 8-bit integer use for BOOL, to distinguish it from U8 - return mlir::IntegerType::get(ctx, 8, mlir::IntegerType::Signless); -} - -mlir::Type importPrecision(mlir::MLIRContext* ctx, const ov::element::Type& precision) { - switch (precision) { - case ov::element::Type_t::f64: - return mlir::Float64Type::get(ctx); - case ov::element::Type_t::f32: - return mlir::Float32Type::get(ctx); - case ov::element::Type_t::f16: - return mlir::Float16Type::get(ctx); - case ov::element::Type_t::bf16: - return mlir::BFloat16Type::get(ctx); - case ov::element::Type_t::i64: - return getSInt64Type(ctx); - case ov::element::Type_t::u64: - return getUInt64Type(ctx); - case ov::element::Type_t::i32: - return getSInt32Type(ctx); - case ov::element::Type_t::u32: - return getUInt32Type(ctx); - case ov::element::Type_t::i16: - return getSInt16Type(ctx); - case ov::element::Type_t::u16: - return getUInt16Type(ctx); - case ov::element::Type_t::i8: - return getSInt8Type(ctx); - case ov::element::Type_t::u8: - return getUInt8Type(ctx); - case ov::element::Type_t::i4: - return getSInt4Type(ctx); - case ov::element::Type_t::u4: - return getUInt4Type(ctx); - case ov::element::Type_t::boolean: - return getBool8Type(ctx); - default: - OPENVINO_THROW("Unsupported element_type: ", precision); - } -} - -mlir::RankedTensorType importTensor(mlir::MLIRContext* ctx, - const ov::PartialShape& shape, - const ov::element::Type& elemType) { - return mlir::RankedTensorType::get(ArrayRef(importShape(shape)), importPrecision(ctx, elemType)); -} - -mlir::Location createLocation(mlir::MLIRContext* ctx, NodePtr node) { - return createLayerLocation(ctx, node->get_friendly_name(), node->get_type_name()); -} - -} // namespace - - -namespace std { - -template -size_t getHash(const T& val) { - return std::hash()(val); -} - -template -size_t getHash(const T& val, Args&&... args) { - return llvm::hash_combine(getHash(val), getHash(std::forward(args)...)); -} - -template <> -struct hash> final { - size_t operator()(const ov::Output& out) const { - return getHash(out.get_node(), out.get_index()); - } -}; -} // namespace std +#include "mlir_op.hpp" +#include "convert_common.hpp" +#include "subgraph_tracker.hpp" namespace { +using namespace mlir; +using namespace ov::mlir; + MemRefType convertTensorToMemRef(TensorType tensorType) { ArrayRef shape = tensorType.getShape(); Type elementType = tensorType.getElementType(); @@ -533,7 +102,7 @@ SmallVector get_types_for_values(mlir::MLIRContext* context, const o class ConversionContext { public: using Convertor = std::function; - using NodeOutputMap = std::unordered_map, mlir::Value>; + using NodeOutputMap = std::map, mlir::Value>; static const std::map convertors; mlir::MLIRContext* context; @@ -667,198 +236,6 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, } -using InputVector = std::vector>; - - -struct Subgraph { - ov::NodeVector nodes; - ov::OutputVector inputs; - ov::OutputVector outputs; - std::vector output_consumers; - - // Consumes other subgraph - void merge (Subgraph& other) { - nodes.insert(nodes.end(), other.nodes.begin(), other.nodes.end()); - } -}; - - -using SubgraphPtr = std::shared_ptr; -using SubgraphID = SymbolPtr; - - -class SubgraphTracker { -public: - - // callback to finalize the subgraph when it is terminated - using Finalizer = std::function; - - SubgraphTracker(Finalizer finalizer): m_finalizer(finalizer) {} - - void add_node (NodePtr node, bool belongs) { - // collect all subgraph ids that input nodes belong to and all dependencies - Dependencies input_subgraphs; - Dependencies input_dependencies; - for(auto input_value: node->input_values()) { - auto node = input_value.get_node_shared_ptr(); - if(auto id = get_subgraph_id(node)) { - input_subgraphs.insert(ov::symbol::ancestor_of(id)); - } - const auto& deps = get_dependencies(node); - for(auto dep: deps) { - input_dependencies.insert(ov::symbol::ancestor_of(dep)); - } - } - - if(belongs) { - // Below we refuse to merge subgraphs if all of them cannot merge to a single subgraph, this is rough because - // there are cases when part of the input subgraphs can consume the node and others will come as inputs -- TODO. - // TODO: leave only those input subgraphs that are not conflicting with other subgraphs nor with any dependencies - if(input_subgraphs.empty() || intersected(input_subgraphs, input_dependencies)) { // no input subgraphs || cannot merge all due to cycles - try_terminate_subgraphs(input_subgraphs, node); - - // start a new subgraph - auto subgraph_id = new_subgraph(); - add_node_to_subgraph(node, subgraph_id); - set_subgraph_id(node, subgraph_id); - input_dependencies.insert(input_subgraphs.begin(), input_subgraphs.end()); - } else { - auto merged_subgraph_id = std::accumulate( - input_subgraphs.begin(), - input_subgraphs.end(), - *input_subgraphs.begin(), - [this](SubgraphID a, SubgraphID b) { - merge_subgraphs(a, b); - return a; - } - ); - set_subgraph_id(node, merged_subgraph_id); - add_node_to_subgraph(node, merged_subgraph_id); - } - - } else { - try_terminate_subgraphs(input_subgraphs, node); - set_subgraph_id(node, nullptr); - input_dependencies.insert(input_subgraphs.begin(), input_subgraphs.end()); - } - set_dependencies(node, input_dependencies); - } - - void finalize() { - for(auto subgraph_record: m_subgraphs) { - terminate_subgraph(subgraph_record.first); - } - } - -private: - - std::unordered_map m_subgraphs; - using Dependencies = std::unordered_set; - Finalizer m_finalizer; - - // // Detects if `node` depends on a node from `subgraph` but goes via node that doesn't belongs to `subgraph` - // bool depends_via_break (NodePtr node, const Subgraph& subgraph); - - SubgraphID new_subgraph() { - SubgraphID id = std::make_shared(); - m_subgraphs[id] = std::make_shared(); - return id; - } - - void add_node_to_subgraph(NodePtr node, SubgraphID id) { - get_subgraph(id)->nodes.push_back(node); - } - - void merge_subgraphs(SubgraphID id1, SubgraphID id2) { - id1 = ov::symbol::ancestor_of(id1); - id2 = ov::symbol::ancestor_of(id2); - if (id1 == id2) return; - - auto subgraph1 = get_subgraph(id1); - auto subgraph2 = get_subgraph(id2); - subgraph1->merge(*subgraph2); - m_subgraphs.erase(id1); - m_subgraphs.erase(id2); - ov::symbol::set_equal(id1, id2); - id1 = ov::symbol::ancestor_of(id1); - m_subgraphs[id1] = subgraph1; - } - - SubgraphPtr get_subgraph(SubgraphID id) { - return m_subgraphs.at(ov::symbol::ancestor_of(id)); - } - - // set/get all subgraph ids that contribute to a given node - - const Dependencies& get_dependencies(NodePtr node) { - return node->get_rt_info().at("__subgraph_dependencies").as(); - } - void set_dependencies(NodePtr node, const Dependencies& dependencies) { - node->get_rt_info()["__subgraph_dependencies"] = dependencies; - } - - // set/get subgraph id that a give node belongs to - - SubgraphID get_subgraph_id(NodePtr node) { - auto id = node->get_rt_info().at("__subgraph_id").as(); - if(id) { - id = ov::symbol::ancestor_of(id); - } - return id; - } - - void set_subgraph_id(NodePtr node, SubgraphID id) { - node->get_rt_info()["__subgraph_id"] = id; - } - - bool intersected(const Dependencies& a, const Dependencies& b) { - for(const auto& x: a) { - if(b.count(x)) - return true; - } - return false; - } - - void terminate_subgraph(SubgraphID id) { - id = ov::symbol::ancestor_of(id); - auto subgraph = get_subgraph(id); - // Build subgraph inputs and outputs - std::unordered_set> inputs; - auto& outputs = subgraph->outputs; - auto& output_consumers = subgraph->output_consumers; - for(auto node: subgraph->nodes) { - for(auto input: node->input_values()) { - auto input_id = get_subgraph_id(input.get_node_shared_ptr()); - if(!ov::symbol::are_equal(id, input_id)) { - inputs.insert(input); - } - } - for(auto output: node->outputs()) { - const auto& consumers = output.get_target_inputs(); - InputVector external_consumers; - for(auto consumer: consumers) { - auto consumer_id = get_subgraph_id(consumer.get_node()->shared_from_this()); - if(!ov::symbol::are_equal(id, consumer_id)) { - external_consumers.push_back(consumer); - } - } - bool used_outside = !external_consumers.empty(); - if(used_outside) { - outputs.push_back(output); - output_consumers.push_back(external_consumers); - } - } - } - subgraph->inputs.assign(inputs.begin(), inputs.end()); - m_finalizer(subgraph); - } - - void try_terminate_subgraphs(const Dependencies& subgraphs, NodePtr terminator) { - // TODO: Terminate subgraphs earlier when all terminating nodes are known - // TODO: try to merge subgraphs if they are being terminated simultaniously - } -}; - // This pass find marked with a special flag group of nodes and collapse each group to a single MLIR function NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph) { diff --git a/src/common/transformations/src/transformations/mlir/convert_common.cpp b/src/common/transformations/src/transformations/mlir/convert_common.cpp new file mode 100644 index 00000000000000..f7c917af1a23cb --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/convert_common.cpp @@ -0,0 +1,132 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "convert_common.hpp" + +namespace { + +using namespace mlir; + +IntegerType getSInt4Type(MLIRContext* ctx) { + return IntegerType::get(ctx, 4, IntegerType::Signed); +} + +IntegerType getSInt8Type(MLIRContext* ctx) { + return IntegerType::get(ctx, 8, IntegerType::Signed); +} + +IntegerType getSInt16Type(MLIRContext* ctx) { + return IntegerType::get(ctx, 16, IntegerType::Signed); +} + +IntegerType getSInt32Type(MLIRContext* ctx) { + return IntegerType::get(ctx, 32, IntegerType::Signed); +} + +IntegerType getSInt64Type(MLIRContext* ctx) { + return IntegerType::get(ctx, 64, IntegerType::Signed); +} + +IntegerType getUInt4Type(MLIRContext* ctx) { + return IntegerType::get(ctx, 4, IntegerType::Unsigned); +} + +IntegerType getUInt8Type(MLIRContext* ctx) { + return IntegerType::get(ctx, 8, IntegerType::Unsigned); +} + +IntegerType getUInt16Type(MLIRContext* ctx) { + return IntegerType::get(ctx, 16, IntegerType::Unsigned); +} + +IntegerType getUInt32Type(MLIRContext* ctx) { + return IntegerType::get(ctx, 32, IntegerType::Unsigned); +} + +IntegerType getUInt64Type(MLIRContext* ctx) { + return IntegerType::get(ctx, 64, IntegerType::Unsigned); +} + +IntegerType getBool8Type(MLIRContext* ctx) { + // Signless 8-bit integer use for BOOL, to distinguish it from U8 + return IntegerType::get(ctx, 8, IntegerType::Signless); +} + +} + +namespace ov { +namespace mlir { + + +Location createLayerLocation(MLIRContext* ctx, const std::string& layerName, const std::string& layerType) { + const auto layerNameAttr = StringAttr::get(ctx, layerName); + const auto nameLoc = NameLoc::get(layerNameAttr); + + SmallVector fields; + fields.emplace_back(StringAttr::get(ctx, "type"), StringAttr::get(ctx, layerType)); + fields.emplace_back(StringAttr::get(ctx, "name"), layerNameAttr); + auto metadata = DictionaryAttr::get(ctx, fields); + + return FusedLoc::get(ctx, {nameLoc}, metadata); +} + +SmallVector importShape(const ov::PartialShape& shape) { + SmallVector out(shape.rank().get_length()); + // TODO: Add support for dynamically ranked shapes + for (size_t i = 0; i < out.size(); ++i) { + const auto& dim = shape[i]; + out[i] = dim.is_static() ? dim.get_length() : ShapedType::kDynamic; + } + return out; +} + +Type importPrecision(MLIRContext* ctx, const ov::element::Type& precision) { + switch (precision) { + case ov::element::Type_t::f64: + return Float64Type::get(ctx); + case ov::element::Type_t::f32: + return Float32Type::get(ctx); + case ov::element::Type_t::f16: + return Float16Type::get(ctx); + case ov::element::Type_t::bf16: + return BFloat16Type::get(ctx); + case ov::element::Type_t::i64: + return getSInt64Type(ctx); + case ov::element::Type_t::u64: + return getUInt64Type(ctx); + case ov::element::Type_t::i32: + return getSInt32Type(ctx); + case ov::element::Type_t::u32: + return getUInt32Type(ctx); + case ov::element::Type_t::i16: + return getSInt16Type(ctx); + case ov::element::Type_t::u16: + return getUInt16Type(ctx); + case ov::element::Type_t::i8: + return getSInt8Type(ctx); + case ov::element::Type_t::u8: + return getUInt8Type(ctx); + case ov::element::Type_t::i4: + return getSInt4Type(ctx); + case ov::element::Type_t::u4: + return getUInt4Type(ctx); + case ov::element::Type_t::boolean: + return getBool8Type(ctx); + default: + OPENVINO_THROW("Unsupported element_type: ", precision); + } +} + +RankedTensorType importTensor(MLIRContext* ctx, + const ov::PartialShape& shape, + const ov::element::Type& elemType) { + return RankedTensorType::get(ArrayRef(importShape(shape)), importPrecision(ctx, elemType)); +} + +Location createLocation(MLIRContext* ctx, NodePtr node) { + return createLayerLocation(ctx, node->get_friendly_name(), node->get_type_name()); +} + +} // namespace mlir +} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/convert_common.hpp b/src/common/transformations/src/transformations/mlir/convert_common.hpp new file mode 100644 index 00000000000000..ac3b217739c4e8 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/convert_common.hpp @@ -0,0 +1,33 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Location.h" + +#include "typedefs.hpp" + + +namespace ov { +namespace mlir { + +using namespace ::mlir; + +Location createLayerLocation(MLIRContext* ctx, const std::string& layerName, const std::string& layerType); + +SmallVector importShape(const ov::PartialShape& shape); + +Type importPrecision(MLIRContext* ctx, const ov::element::Type& precision); + +RankedTensorType importTensor(MLIRContext* ctx, + const ov::PartialShape& shape, + const ov::element::Type& elemType); + +Location createLocation(MLIRContext* ctx, NodePtr node); + +} // namespace mlir +} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp new file mode 100644 index 00000000000000..2e71e5948fafec --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -0,0 +1,323 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir_op.hpp" + +#include +#include +#include +#include +#include + +#include "mlir/Dialect/Bufferization/Transforms/Passes.h" +#include "mlir/Pass/PassManager.h" + +// TODO: Prune unused headers -- it's hard to understand needed ones +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include "mlir/Dialect/Arith/IR/Arith.h" +#include "mlir/Dialect/Arith/Transforms/Passes.h" +#include "mlir/Dialect/Bufferization/Transforms/Passes.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Linalg/TransformOps/DialectExtension.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/Dialect/Tensor/TransformOps/TensorTransformOps.h" +#include "mlir/Dialect/Vector/IR/VectorOps.h" +#include "mlir/ExecutionEngine/ExecutionEngine.h" +#include "mlir/ExecutionEngine/JitRunner.h" +#include "mlir/ExecutionEngine/OptUtils.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/Dialect.h" +#include "mlir/IR/Location.h" +#include "mlir/IR/ValueRange.h" +#include "mlir/InitAllDialects.h" +#include "mlir/InitAllExtensions.h" +#include "mlir/InitAllPasses.h" +#include "mlir/Parser/Parser.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Support/LLVM.h" +#include "mlir/Target/LLVMIR/Dialect/All.h" +#include "mlir/Target/LLVMIR/Export.h" +#include "mlir/Target/LLVMIR/ModuleTranslation.h" + +namespace { + +using namespace mlir; + +using NodePtr = std::shared_ptr; +using SymbolPtr = std::shared_ptr; + +void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module) { + // A set of default passes that lower any input IR to LLVM + PassManager pm(module->getContext()); + +#if 0 // TODO: if TPP is available + + tpp::DefaultPipelineOptions defPipelineOpts{defGpuBackend}; + pm.addPass(tpp::createDefaultPipeline(defPipelineOpts)); + +#else // Simplified default lowering to LLVM from LLVM tests + + // Remove empty tensors to avoid converting them into temporary buffers. + pm.addPass(bufferization::createEmptyTensorEliminationPass()); + + pm.addPass(bufferization::createOneShotBufferizePass()); + // TODO: Add deallocation pass/pipeline to avoid memory leaks. + + // Cleanup after bufferization - possibly remove redundant copies. + pm.addNestedPass(createCanonicalizerPass()); + pm.addNestedPass(createCSEPass()); + + // Blanket-convert any remaining high-level vector ops to loops if any remain. + pm.addNestedPass(createConvertVectorToSCFPass()); + // pm.addNestedPass(createLinalgGeneralizeNamedOpsPass()); + // Blanket-convert any remaining linalg ops to loops if any remain. + pm.addNestedPass(createConvertLinalgToLoopsPass()); + // Blanket-convert any remaining affine ops if any remain. + pm.addPass(createLowerAffinePass()); + // Convert SCF to CF (always needed). + pm.addPass(createConvertSCFToCFPass()); + // Sprinkle some cleanups. + pm.addPass(createCanonicalizerPass()); + pm.addPass(createCSEPass()); + // Blanket-convert any remaining linalg ops to LLVM if any remain. + // pm.addPass(createConvertLinalgToLLVMPass()); // no such pass + // Convert vector to LLVM (always needed). + pm.addPass(createConvertVectorToLLVMPass()); + // Convert Math to LLVM (always needed). + pm.addNestedPass(createConvertMathToLLVMPass()); + // Expand complicated MemRef operations before lowering them. + pm.addPass(memref::createExpandStridedMetadataPass()); + // The expansion may create affine expressions. Get rid of them. + pm.addPass(createLowerAffinePass()); + // Convert MemRef to LLVM (always needed). + // pm.addPass(memref::createExpandOpsPass()); + pm.addPass(createFinalizeMemRefToLLVMConversionPass()); + // Convert Func to LLVM (always needed). + pm.addPass(createConvertFuncToLLVMPass()); + // Convert Index to LLVM (always needed). + pm.addPass(createConvertIndexToLLVMPass()); + // Convert remaining unrealized_casts (always needed). + pm.addPass(createReconcileUnrealizedCastsPass()); + +#endif + + auto result = pm.run(module.get()); + if (failed(result)) { + llvm::errs() << "ERROR: Failed to lower IR to LLVM dialect\n"; + module->print(llvm::errs()); + } +} + +std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext& llvmContext) { + // Default lowering for mlir-cpu-runner + auto llvmModule = translateModuleToLLVMIR(module, llvmContext); + assert(llvmModule); + + // Target machine, null if not specified + std::unique_ptr targetMachine; + + std::string triple = "x86_64-linux-gnu"; + std::string cpuName = "alderlake"; // sapphirerapids, nehalem, etc. + std::string fpuName = "avx2"; // sse4.2, avx, avx2, avx512bf16, etc. + bool printLLVM = false; + auto codeGenOpt = 2; + + // Specify target machine + if (!triple.empty() && !cpuName.empty()) { + std::string error; + const llvm::Target* target = llvm::TargetRegistry::lookupTarget(triple, error); + if (!target) { + llvm::errs() << "Error while looking up target triple: "; + llvm::errs() << error << "\n"; + return nullptr; + } + + // These options should force fused MLA, but they don't. :/ + // Adding unsafe math attribute to functions below do the trick. + llvm::TargetOptions targetOptions; + targetOptions.UnsafeFPMath = true; + targetOptions.AllowFPOpFusion = llvm::FPOpFusion::FPOpFusionMode::Fast; + targetMachine.reset(target->createTargetMachine(triple, + cpuName, + "+" + fpuName, + targetOptions, + /* reloc model */ std::nullopt, + /* code model */ std::nullopt, + llvm::CodeGenOptLevel(codeGenOpt))); + if (!targetMachine) { + llvm::errs() << "Error while looking up target CPU: "; + llvm::errs() << cpuName << "\n"; + return nullptr; + } + } + + // Run the optimized pipeline + int sizeLevel = 0; + auto optPipeline = makeOptimizingTransformer(codeGenOpt, sizeLevel, targetMachine.get()); + if (auto err = optPipeline(llvmModule.get())) { + llvmModule->print(llvm::errs(), nullptr); + llvm::errs() << "Error while passing through the LLVM pipeline: "; + llvm::errs() << err << "\n"; + return nullptr; + } + + // MLIR doesn't lower LLVM with fast-math flags, but we need that, so we + // add for each function, to get FMAs and other goodies. + for (auto& func : llvmModule->functions()) { + func.addFnAttr("unsafe-fp-math", "true"); + } + + if (printLLVM) + llvmModule->print(llvm::outs(), nullptr); + + return llvmModule; +} + +// TODO: u4/i4 types are not supported +struct MemRef { + MemRef() = default; + + MemRef(ov::Tensor tensor) + : allocated(tensor.data()), + aligned(tensor.data()), + offset(0), + shape(tensor.get_shape().begin(), tensor.get_shape().end()) { + strides.resize(tensor.get_shape().size()); + const auto& byte_strides = tensor.get_strides(); + auto element_size = tensor.get_element_type().size(); + for (size_t i = 0; i < strides.size(); ++i) { + assert(byte_strides[i] % element_size == 0); + // TODO: handle case when stride is not aligned (restrict at OV API level) + strides[i] = byte_strides[i] / element_size; + //std::cout << "stride [" << i << "] = " << strides[i] << "\n"; + } + } + + void* allocated; + void* aligned; + int64_t offset; + std::vector shape; + std::vector strides; + + void append_to_packed_args(std::vector& args) { + args.push_back(&allocated); + args.push_back(&aligned); + args.push_back(&offset); + for (size_t i = 0; i < shape.size(); ++i) { + args.push_back(&shape[i]); + } + for (size_t i = 0; i < strides.size(); ++i) { + args.push_back(&strides[i]); + } + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ::mlir; + + +MLIREvaluate::MLIREvaluate(OwningOpRef _module) : module(std::move(_module)) { + if (true) { + std::cout << "[ DEBUG ] Source MLIR:\n"; + std::cerr << "-----------------------------------------\n"; + module->dump(); + std::cout << "-----------------------------------------\n"; + } + + prepareMLIRKernelWithoutWrapper(module); + + if (true) { + std::cerr << "[ DEBUG ] Target LLVM:\n"; + std::cerr << "-----------------------------------------\n"; + module->dump(); + std::cerr << "-----------------------------------------\n"; + } + + auto optPipeline = mlir::makeOptimizingTransformer(2, + /*sizeLevel=*/0, // FIXME: HARDCODED + /*targetMachine=*/nullptr); + + mlir::ExecutionEngineOptions engineOptions; + engineOptions.transformer = optPipeline; // opt level looks to be overriden in lowerToLLVMIR, but is still used + // in `create` independently + engineOptions.llvmModuleBuilder = lowerToLLVMIR; + auto maybeEngine = mlir::ExecutionEngine::create(module.get(), engineOptions); + if (maybeEngine) { + engine = std::move(maybeEngine.get()); + } else { + llvm::errs() << "failed to construct an execution engine\n"; + abort(); + } +} + +bool MLIREvaluate::invoke_packed(std::vector& args) { + auto invocationResult = engine->invokePacked("entry", args); + if (invocationResult) { + llvm::errs() << "JIT invocation failed\n"; + return false; + } + return true; +} + +MLIROp::MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types) + : Op(args), + engine(engine), + output_types(output_types) { + constructor_validate_and_infer_types(); +} + +void MLIROp::validate_and_infer_types() { + set_output_size(output_types.size()); + for (size_t i = 0; i < output_types.size(); ++i) { + set_output_type(i, std::get<0>(output_types[i]), std::get<1>(output_types[i])); + } +} + +NodePtr MLIROp::clone_with_new_inputs(const ov::OutputVector& new_args) const { + return std::make_shared(new_args, engine, output_types); +} + +bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const { + outputs[0].set_shape(inputs[0].get_shape()); + + std::vector memref_args; + for (size_t i = 0; i < inputs.size(); ++i) { + memref_args.push_back(MemRef(inputs[i])); + } + for (size_t i = 0; i < outputs.size(); ++i) { + memref_args.push_back(MemRef(outputs[i])); + } + std::vector args; + + std::for_each(memref_args.begin(), memref_args.end(), [&args](MemRef& x) { + x.append_to_packed_args(args); + }); + + std::cerr << "[ INFO ] Running kernel in MLIROp::evaluate\n"; + return engine->invoke_packed(args); +} + +bool MLIROp::has_evaluate() const { + return true; +} + +} // namespace mlir +} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.hpp b/src/common/transformations/src/transformations/mlir/mlir_op.hpp new file mode 100644 index 00000000000000..f1c36bbfa4146b --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/mlir_op.hpp @@ -0,0 +1,53 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/OwningOpRef.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/ExecutionEngine/ExecutionEngine.h" +#include "mlir/ExecutionEngine/JitRunner.h" +#include "mlir/ExecutionEngine/OptUtils.h" + +#include "openvino/op/op.hpp" + +#include "convert_common.hpp" + + +namespace ov { +namespace mlir { + +using ::mlir::OwningOpRef; +using ::mlir::ModuleOp; +using ::mlir::ExecutionEngine; +using ::mlir::ModuleOp; + +class MLIREvaluate { + OwningOpRef module; // FIXME: needs to be kept? + std::unique_ptr engine; + +public: + + MLIREvaluate(OwningOpRef _module); + bool invoke_packed(std::vector& args); +}; + + +class OPENVINO_API MLIROp : public ov::op::Op { + std::shared_ptr engine; + OVOutputTypes output_types; + +public: + + OPENVINO_OP("MLIROp"); + + MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types); + void validate_and_infer_types() override; + NodePtr clone_with_new_inputs(const ov::OutputVector& new_args) const override; + bool evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const override; + bool has_evaluate() const override; +}; + +} // namespace mlir +} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp b/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp new file mode 100644 index 00000000000000..730f56b402a3a8 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp @@ -0,0 +1,179 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include + +#include "subgraph_tracker.hpp" + + +namespace ov { +namespace mlir { + + +void Subgraph::merge (Subgraph& other) { + nodes.insert(nodes.end(), other.nodes.begin(), other.nodes.end()); +} + + +SubgraphTracker::SubgraphTracker(Finalizer finalizer): m_finalizer(finalizer) {} + +void SubgraphTracker::add_node (NodePtr node, bool belongs) { + // collect all subgraph ids that input nodes belong to and all dependencies + Dependencies input_subgraphs; + Dependencies input_dependencies; + for(auto input_value: node->input_values()) { + auto node = input_value.get_node_shared_ptr(); + if(auto id = get_subgraph_id(node)) { + input_subgraphs.insert(ov::symbol::ancestor_of(id)); + } + const auto& deps = get_dependencies(node); + for(auto dep: deps) { + input_dependencies.insert(ov::symbol::ancestor_of(dep)); + } + } + + if(belongs) { + // Below we refuse to merge subgraphs if all of them cannot merge to a single subgraph, this is rough because + // there are cases when part of the input subgraphs can consume the node and others will come as inputs -- TODO. + // TODO: leave only those input subgraphs that are not conflicting with other subgraphs nor with any dependencies + if(input_subgraphs.empty() || intersected(input_subgraphs, input_dependencies)) { // no input subgraphs || cannot merge all due to cycles + try_terminate_subgraphs(input_subgraphs, node); + + // start a new subgraph + auto subgraph_id = new_subgraph(); + add_node_to_subgraph(node, subgraph_id); + set_subgraph_id(node, subgraph_id); + input_dependencies.insert(input_subgraphs.begin(), input_subgraphs.end()); + } else { + auto merged_subgraph_id = std::accumulate( + input_subgraphs.begin(), + input_subgraphs.end(), + *input_subgraphs.begin(), + [this](SubgraphID a, SubgraphID b) { + merge_subgraphs(a, b); + return a; + } + ); + set_subgraph_id(node, merged_subgraph_id); + add_node_to_subgraph(node, merged_subgraph_id); + } + + } else { + try_terminate_subgraphs(input_subgraphs, node); + set_subgraph_id(node, nullptr); + input_dependencies.insert(input_subgraphs.begin(), input_subgraphs.end()); + } + set_dependencies(node, input_dependencies); +} + +void SubgraphTracker::finalize() { + for(auto subgraph_record: m_subgraphs) { + terminate_subgraph(subgraph_record.first); + } +} + +SubgraphID SubgraphTracker::new_subgraph() { + SubgraphID id = std::make_shared(); + m_subgraphs[id] = std::make_shared(); + return id; +} + +void SubgraphTracker::add_node_to_subgraph(NodePtr node, SubgraphID id) { + get_subgraph(id)->nodes.push_back(node); +} + +void SubgraphTracker::merge_subgraphs(SubgraphID id1, SubgraphID id2) { + id1 = ov::symbol::ancestor_of(id1); + id2 = ov::symbol::ancestor_of(id2); + if (id1 == id2) return; + + auto subgraph1 = get_subgraph(id1); + auto subgraph2 = get_subgraph(id2); + subgraph1->merge(*subgraph2); + m_subgraphs.erase(id1); + m_subgraphs.erase(id2); + ov::symbol::set_equal(id1, id2); + id1 = ov::symbol::ancestor_of(id1); + m_subgraphs[id1] = subgraph1; +} + +SubgraphPtr SubgraphTracker::get_subgraph(SubgraphID id) { + return m_subgraphs.at(ov::symbol::ancestor_of(id)); +} + +// set/get all subgraph ids that contribute to a given node + +const SubgraphTracker::Dependencies& SubgraphTracker::get_dependencies(NodePtr node) { + return node->get_rt_info().at("__subgraph_dependencies").as(); +} +void SubgraphTracker::set_dependencies(NodePtr node, const Dependencies& dependencies) { + node->get_rt_info()["__subgraph_dependencies"] = dependencies; +} + +// set/get subgraph id that a give node belongs to + +SubgraphID SubgraphTracker::get_subgraph_id(NodePtr node) { + auto id = node->get_rt_info().at("__subgraph_id").as(); + if(id) { + id = ov::symbol::ancestor_of(id); + } + return id; +} + +void SubgraphTracker::set_subgraph_id(NodePtr node, SubgraphID id) { + node->get_rt_info()["__subgraph_id"] = id; +} + +bool SubgraphTracker::intersected(const Dependencies& a, const Dependencies& b) { + for(const auto& x: a) { + if(b.count(x)) + return true; + } + return false; +} + +void SubgraphTracker::terminate_subgraph(SubgraphID id) { + id = ov::symbol::ancestor_of(id); + auto subgraph = get_subgraph(id); + // Build subgraph inputs and outputs + std::set> inputs; + auto& outputs = subgraph->outputs; + auto& output_consumers = subgraph->output_consumers; + for(auto node: subgraph->nodes) { + for(auto input: node->input_values()) { + auto input_id = get_subgraph_id(input.get_node_shared_ptr()); + if(!ov::symbol::are_equal(id, input_id)) { + inputs.insert(input); + } + } + for(auto output: node->outputs()) { + const auto& consumers = output.get_target_inputs(); + InputVector external_consumers; + for(auto consumer: consumers) { + auto consumer_id = get_subgraph_id(consumer.get_node()->shared_from_this()); + if(!ov::symbol::are_equal(id, consumer_id)) { + external_consumers.push_back(consumer); + } + } + bool used_outside = !external_consumers.empty(); + if(used_outside) { + outputs.push_back(output); + output_consumers.push_back(external_consumers); + } + } + } + subgraph->inputs.assign(inputs.begin(), inputs.end()); + m_finalizer(subgraph); +} + +void SubgraphTracker::try_terminate_subgraphs(const Dependencies& subgraphs, NodePtr terminator) { + // TODO: Terminate subgraphs earlier when all terminating nodes are known + // TODO: try to merge subgraphs if they are being terminated simultaniously +} + + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp b/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp new file mode 100644 index 00000000000000..19abadcd602f0b --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp @@ -0,0 +1,66 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "typedefs.hpp" + + +namespace ov { +namespace mlir { + +struct Subgraph { + ov::NodeVector nodes; + ov::OutputVector inputs; + ov::OutputVector outputs; + std::vector output_consumers; + + // Consumes other subgraph + void merge (Subgraph& other); +}; + + +using SubgraphPtr = std::shared_ptr; +using SubgraphID = SymbolPtr; + + +class SubgraphTracker { +public: + + // callback to finalize the subgraph when it is terminated + using Finalizer = std::function; + + SubgraphTracker(Finalizer finalizer); + void add_node (NodePtr node, bool belongs); + void finalize(); + +private: + + std::unordered_map m_subgraphs; + using Dependencies = std::unordered_set; + Finalizer m_finalizer; + + SubgraphID new_subgraph(); + void add_node_to_subgraph(NodePtr node, SubgraphID id); + void merge_subgraphs(SubgraphID id1, SubgraphID id2); + SubgraphPtr get_subgraph(SubgraphID id); + + // set/get all subgraph ids that contribute to a given node + const Dependencies& get_dependencies(NodePtr node); + void set_dependencies(NodePtr node, const Dependencies& dependencies); + + // set/get subgraph id that a give node belongs to + SubgraphID get_subgraph_id(NodePtr node); + void set_subgraph_id(NodePtr node, SubgraphID id); + + bool intersected(const Dependencies& a, const Dependencies& b); + void terminate_subgraph(SubgraphID id); + void try_terminate_subgraphs(const Dependencies& subgraphs, NodePtr terminator); +}; + + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/typedefs.hpp b/src/common/transformations/src/transformations/mlir/typedefs.hpp new file mode 100644 index 00000000000000..a773b9aa76884b --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/typedefs.hpp @@ -0,0 +1,20 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/core/node.hpp" +#include "openvino/core/symbol.hpp" + + +namespace ov { +namespace mlir { + +using NodePtr = std::shared_ptr; +using SymbolPtr = std::shared_ptr; +using OVOutputTypes = std::vector>; +using InputVector = std::vector>; + +} // namespace mlir +} // namespace ov \ No newline at end of file From e30e919f6229a2794a01efdd12d2c10e4ee7bc44 Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Fri, 12 Jul 2024 14:12:01 +0200 Subject: [PATCH 007/121] Pre-bufferization cleanup (#138) Adds cleanup right before bufferization to eliminate temporary buffer creation in multi-node pattern lowering. --- .../transformations/src/transformations/mlir/mlir_op.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 2e71e5948fafec..5d833df6d20db6 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -71,6 +71,11 @@ void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module) #else // Simplified default lowering to LLVM from LLVM tests + // Cleanup before bufferization. + // Simplifies IR to allow better bufferization. + pm.addNestedPass(createCanonicalizerPass()); + pm.addNestedPass(createCSEPass()); + // Remove empty tensors to avoid converting them into temporary buffers. pm.addPass(bufferization::createEmptyTensorEliminationPass()); From 754e385ae4aceac197e6869fd103329dab969745 Mon Sep 17 00:00:00 2001 From: Sergey Lyalin Date: Tue, 16 Jul 2024 21:26:34 +0400 Subject: [PATCH 008/121] ov::MatMul -> linalg::MatmulTransposeBOp (#139) * Moved ConversionContext to a separate file * Moved MarkPattern and associated helpers to conversion_context. * FIXME: One particular case of MatMul -> linalg::MatmulTransposeBOp conversion. Currently has a hack in output tensor allocation in common code, works with MatMul of specific size. * Use fill to prepare output for MatMul result, fixed dynamic output dimensions (transpose_b wasn't handled). * Fix getConstant: swap int and real parts * Removed redundant Value(...) * Small clarification in a comment * Generic mapping of dynamic dimensions from input to output of MLIROp during inference, correct output tensor allocation based on that functionality. Removed related hack in MLIROp::evaluate. --- .../mlir/conversion_context.cpp | 92 ++++++++++++ .../mlir/conversion_context.hpp | 64 ++++++++ .../src/transformations/mlir/convert.cpp | 139 ++++++------------ .../transformations/mlir/convert_common.hpp | 16 ++ .../src/transformations/mlir/mlir_op.cpp | 29 +++- .../src/transformations/mlir/mlir_op.hpp | 7 +- .../src/transformations/mlir/op/matmul.cpp | 66 +++++++++ .../src/transformations/mlir/op/matmul.hpp | 24 +++ .../transformations/mlir/subgraph_tracker.cpp | 4 +- 9 files changed, 336 insertions(+), 105 deletions(-) create mode 100644 src/common/transformations/src/transformations/mlir/conversion_context.cpp create mode 100644 src/common/transformations/src/transformations/mlir/conversion_context.hpp create mode 100644 src/common/transformations/src/transformations/mlir/op/matmul.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/matmul.hpp diff --git a/src/common/transformations/src/transformations/mlir/conversion_context.cpp b/src/common/transformations/src/transformations/mlir/conversion_context.cpp new file mode 100644 index 00000000000000..5d1dfbf619a9b0 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/conversion_context.cpp @@ -0,0 +1,92 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +// #include "mlir/IR/BuiltinAttributes.h" +// #include "mlir/IR/BuiltinTypes.h" + +#include "conversion_context.hpp" + + +namespace ov { +namespace mlir { + +using namespace ::mlir; + + +std::string ConversionContext::rt_info_convertor () { + return "__mlir_convertor"; +} + + +ConversionContext::ConversionContext(mlir::MLIRContext* context, mlir::OpBuilder* block_builder) + : context(context), + block_builder(block_builder) {} + +SmallVector ConversionContext::getInputs(NodePtr node) { + SmallVector out; + out.reserve(node->get_input_size()); + for (const auto& input : node->inputs()) { + out.push_back(nodeOutputMap.at(input.get_source_output())); + } + return out; +} + +void ConversionContext::addOutputs(NodePtr node, mlir::Operation* op) { + const auto results = op->getOpResults(); + + OPENVINO_ASSERT( + results.size() == node->get_output_size(), + "Mismatch between original Node '{0}' number of outputs '{1}' and created number of outputs '{2}'", + node->get_friendly_name(), + node->get_output_size(), + results.size()); + + for (const auto& res : results) { + nodeOutputMap.emplace(node->output(res.getResultNumber()), res); + } +} + +void ConversionContext::convert(NodePtr node) { + auto convertor = node->get_rt_info()[rt_info_convertor()].as(); + convertor(*this, node); +} + +void ConversionContext::set_convertor(NodePtr node, const Convertor& convertor) { + Convertor local_copy = convertor; + auto as_any = ov::Any(local_copy); + node->get_rt_info()[rt_info_convertor()] = as_any; +} + + + +const std::string& subgraph_mark() { + static const std::string mark = "__subgraph_mlir_mark"; + return mark; +} + +void set_subgraph_mark(NodePtr node) { + node->get_rt_info()[subgraph_mark()]; +} + +bool get_subgraph_mark(NodePtr node) { + return node->get_rt_info().count(subgraph_mark()); +} + + +MarkPattern::MarkPattern(NodePtr pattern, ConversionContext::Convertor convertor) { + auto callback = [convertor](ov::pass::pattern::Matcher& m) { + // TODO: support multi-node patterns marking + auto node = m.get_match_root(); + set_subgraph_mark(node); + ConversionContext::set_convertor(node, convertor); + return true; + }; + + auto m = std::make_shared(pattern, "MarkPattern"); + register_matcher(m, callback); +} + + +} // namespace mlir +} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/conversion_context.hpp b/src/common/transformations/src/transformations/mlir/conversion_context.hpp new file mode 100644 index 00000000000000..3f2c0ee9b34619 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/conversion_context.hpp @@ -0,0 +1,64 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "mlir/IR/Value.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Builders.h" + +#include "typedefs.hpp" + +namespace ov { +namespace mlir { + +using ::mlir::Value; +using ::mlir::MLIRContext; +using ::mlir::OpBuilder; +using ::mlir::Operation; +using ::mlir::SmallVector; + +class ConversionContext { + static std::string rt_info_convertor (); + +public: + using Convertor = std::function; + using NodeOutputMap = std::map, mlir::Value>; + + static const std::map convertors; + mlir::MLIRContext* context; + mlir::OpBuilder* block_builder; + NodeOutputMap nodeOutputMap; + + ConversionContext(mlir::MLIRContext* context, mlir::OpBuilder* block_builder); + + SmallVector getInputs(NodePtr node); + void addOutputs(NodePtr node, mlir::Operation* op); + + mlir::OpBuilder& builder() { + return *block_builder; + } + + static void set_convertor(NodePtr node, const Convertor& convertor); + + void convert(NodePtr node); +}; + + +const std::string& subgraph_mark(); + +void set_subgraph_mark(NodePtr node); + +bool get_subgraph_mark(NodePtr node); + +class MarkPattern : public ov::pass::MatcherPass { +public: + OPENVINO_RTTI("MarkPattern", "0"); + MarkPattern(NodePtr pattern, ConversionContext::Convertor convertor); +}; + +} // namespace mlir +} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index dc2cb8d1910ef7..339d23053627f9 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -66,6 +66,8 @@ #include "mlir_op.hpp" #include "convert_common.hpp" #include "subgraph_tracker.hpp" +#include "conversion_context.hpp" +#include "op/matmul.hpp" namespace { @@ -99,53 +101,6 @@ SmallVector get_types_for_values(mlir::MLIRContext* context, const o return types; } -class ConversionContext { -public: - using Convertor = std::function; - using NodeOutputMap = std::map, mlir::Value>; - - static const std::map convertors; - mlir::MLIRContext* context; - mlir::OpBuilder* block_builder; - NodeOutputMap nodeOutputMap; - - ConversionContext(mlir::MLIRContext* context, mlir::OpBuilder* block_builder) - : context(context), - block_builder(block_builder) {} - - SmallVector getInputs(NodePtr node) { - SmallVector out; - out.reserve(node->get_input_size()); - for (const auto& input : node->inputs()) { - out.push_back(nodeOutputMap.at(input.get_source_output())); - } - return out; - } - - void addOutputs(NodePtr node, mlir::Operation* op) { - const auto results = op->getOpResults(); - - OPENVINO_ASSERT( - results.size() == node->get_output_size(), - "Mismatch between original Node '{0}' number of outputs '{1}' and created number of outputs '{2}'", - node->get_friendly_name(), - node->get_output_size(), - results.size()); - - for (const auto& res : results) { - nodeOutputMap.emplace(node->output(res.getResultNumber()), res); - } - } - - mlir::OpBuilder& builder() { - return *block_builder; - } - - void convert(NodePtr node) { - convertors.at(node->get_type_info())(*this, node); - } -}; - template struct ConvertBinary { void operator()(ConversionContext& context, NodePtr node) { @@ -170,12 +125,6 @@ struct ConvertBinary { } }; -const std::map ConversionContext::convertors = { - {ov::op::v1::Add::get_type_info_static(), Convertor(ConvertBinary())}, - {ov::op::v1::Subtract::get_type_info_static(), Convertor(ConvertBinary())}, - {ov::op::v1::Multiply::get_type_info_static(), Convertor(ConvertBinary())}, - {ov::op::v1::Divide::get_type_info_static(), Convertor(ConvertBinary())}, -}; mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, const ov::OutputVector& inputs, @@ -236,56 +185,57 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, } -// This pass find marked with a special flag group of nodes and collapse each group to a single MLIR function +// This pass converts a group of nodes into a single MLIROp NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph) { + mlir::OwningOpRef module = ngraph_to_mlir(context, subgraph->inputs, subgraph->nodes, subgraph->outputs); - mlir::OwningOpRef module; - - module = ngraph_to_mlir(context, subgraph->inputs, subgraph->nodes, subgraph->outputs); + const auto& inputs = subgraph->inputs; + using Index = DimensionsMap::value_type::value_type; + std::map input_map; + for (size_t i = 0; i < inputs.size(); ++i) { + auto input = inputs[i]; + auto shape = input.get_partial_shape(); + for (size_t j = 0; j < shape.size(); ++j) { + auto dim = shape[j]; + if(shape[j].is_dynamic()) { + auto symbol = ov::symbol::ancestor_of(dim.get_symbol()); + if(0 == input_map.count(symbol)) { + input_map[symbol] = Index(i, j); + } else { + std::cerr << "[ DEBUG ] Lost equality constraint for dimensions in output " << input << "\n" + << " If the constraint is violated in runtime it will result in the undefined behaviour.\n"; + } + } + } + } + std::tuple empty(-1, -1); + const auto& outputs = subgraph->outputs; OVOutputTypes output_types; - for (size_t i = 0; i < subgraph->outputs.size(); ++i) { + DimensionsMap output_map; + output_map.reserve(outputs.size()); + for (size_t i = 0; i < outputs.size(); ++i) { + auto output = outputs[i]; + auto shape = output.get_partial_shape(); output_types.push_back( - std::make_tuple(subgraph->outputs[i].get_element_type(), subgraph->outputs[i].get_partial_shape())); + std::make_tuple(output.get_element_type(), shape)); + DimensionsMap::value_type dm; + dm.reserve(shape.size()); + for (size_t j = 0; j < shape.size(); ++j) { + auto dim = shape[j]; + dm.push_back(dim.is_dynamic() ? input_map.at(ov::symbol::ancestor_of(dim.get_symbol())) : empty); + } + output_map.emplace_back(dm); } return std::make_shared( subgraph->inputs, std::make_shared(std::move(module)), - output_types + output_types, + output_map ); }; -const std::string& subgraph_mark() { - static const std::string mark = "__subgraph_mlir_mark"; - return mark; -} - -void set_subgraph_mark(NodePtr node) { - node->get_rt_info()[subgraph_mark()]; -} - -bool get_subgraph_mark(NodePtr node) { - return node->get_rt_info().count(subgraph_mark()); -} - -class MarkPattern : public ov::pass::MatcherPass { -public: - OPENVINO_RTTI("MarkPattern", "0"); - MarkPattern(NodePtr pattern) { - auto callback = [](ov::pass::pattern::Matcher& m) { - // TODO: support multi-node patterns marking - auto node = m.get_match_root(); - set_subgraph_mark(node); - return true; - }; - - auto m = std::make_shared(pattern, "MarkPattern"); - register_matcher(m, callback); - } -}; - - void replace_subgraph(SubgraphPtr subgraph, NodePtr node) { const auto& output_consumers = subgraph->output_consumers; assert(output_consumers.size() == node->get_output_size()); @@ -361,10 +311,11 @@ void injectMLIR(std::shared_ptr model, MLIRContext* context) { using namespace ov::op; manager.set_per_pass_validation(false); manager.register_pass(); - manager.register_pass(elementwise_f32_binary_no_broadcast()); - manager.register_pass(elementwise_f32_binary_no_broadcast()); - manager.register_pass(elementwise_f32_binary_no_broadcast()); - manager.register_pass(elementwise_f32_binary_no_broadcast()); + manager.register_pass(elementwise_f32_binary_no_broadcast(), ConvertBinary()); + manager.register_pass(elementwise_f32_binary_no_broadcast(), ConvertBinary()); + manager.register_pass(elementwise_f32_binary_no_broadcast(), ConvertBinary()); + manager.register_pass(elementwise_f32_binary_no_broadcast(), ConvertBinary()); + manager.register_pass(); manager.register_pass(context); manager.run_passes(model); model->validate_nodes_and_infer_types(); diff --git a/src/common/transformations/src/transformations/mlir/convert_common.hpp b/src/common/transformations/src/transformations/mlir/convert_common.hpp index ac3b217739c4e8..7fd19fe875eb80 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.hpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.hpp @@ -8,6 +8,7 @@ #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/MLIRContext.h" #include "mlir/IR/Location.h" +#include "mlir/Dialect/Arith/IR/Arith.h" #include "typedefs.hpp" @@ -29,5 +30,20 @@ RankedTensorType importTensor(MLIRContext* ctx, Location createLocation(MLIRContext* ctx, NodePtr node); +// Borrowed it from TPP-MLIR. FIXME: Do we have a better upstreamed alternative? +template +mlir::arith::ConstantOp getConstant(OpBuilder &builder, const ov::element::Type& precision, T value) { + auto unkLoc = builder.getUnknownLoc(); + TypedAttr attr; + auto type = importPrecision(builder.getContext(), precision); + if(precision.is_integral()) { + attr = builder.getIntegerAttr(type, int64_t(value)); + } else if(precision.is_real()) { + attr = builder.getFloatAttr(type, double(value)); + } + assert(attr && "Unsupported ConstantOp type"); + return builder.create(unkLoc, type, attr); +} + } // namespace mlir } // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 5d833df6d20db6..8e7c4e1ce7d9db 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -241,10 +241,10 @@ using namespace ::mlir; MLIREvaluate::MLIREvaluate(OwningOpRef _module) : module(std::move(_module)) { if (true) { - std::cout << "[ DEBUG ] Source MLIR:\n"; + std::cerr << "[ DEBUG ] Source MLIR:\n"; std::cerr << "-----------------------------------------\n"; module->dump(); - std::cout << "-----------------------------------------\n"; + std::cerr << "-----------------------------------------\n"; } prepareMLIRKernelWithoutWrapper(module); @@ -282,10 +282,11 @@ bool MLIREvaluate::invoke_packed(std::vector& args) { return true; } -MLIROp::MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types) +MLIROp::MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types, const DimensionsMap& dimensions_map) : Op(args), engine(engine), - output_types(output_types) { + output_types(output_types), + dimensions_map(dimensions_map) { constructor_validate_and_infer_types(); } @@ -297,17 +298,29 @@ void MLIROp::validate_and_infer_types() { } NodePtr MLIROp::clone_with_new_inputs(const ov::OutputVector& new_args) const { - return std::make_shared(new_args, engine, output_types); + return std::make_shared(new_args, engine, output_types, dimensions_map); } bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const { - outputs[0].set_shape(inputs[0].get_shape()); - std::vector memref_args; for (size_t i = 0; i < inputs.size(); ++i) { memref_args.push_back(MemRef(inputs[i])); } for (size_t i = 0; i < outputs.size(); ++i) { + Shape target; + PartialShape expected = get_output_partial_shape(i); + for(size_t j = 0; j < expected.size(); ++j) { + auto dim = expected[j]; + if(dim.is_dynamic()) { + int input_index, dim_index; + std::tie(input_index, dim_index) = dimensions_map[i][j]; + target.push_back(inputs[input_index].get_shape()[dim_index]); + } else { + target.push_back(dim.get_length()); + } + } + //std::cerr << "[ DEBUG ] Set outputs[" << i << "].shape(" << target << ")\n"; + outputs[i].set_shape(target); memref_args.push_back(MemRef(outputs[i])); } std::vector args; @@ -316,7 +329,7 @@ bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) x.append_to_packed_args(args); }); - std::cerr << "[ INFO ] Running kernel in MLIROp::evaluate\n"; + //std::cerr << "[ INFO ] Running kernel in MLIROp::evaluate\n"; return engine->invoke_packed(args); } diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.hpp b/src/common/transformations/src/transformations/mlir/mlir_op.hpp index f1c36bbfa4146b..59248f1d14641d 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.hpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.hpp @@ -34,15 +34,20 @@ class MLIREvaluate { }; +// Maps [output index][dimension index] -> [input index][dimension index] to infer shapes for entire subgraph +using DimensionsMap = std::vector>>; + + class OPENVINO_API MLIROp : public ov::op::Op { std::shared_ptr engine; OVOutputTypes output_types; + DimensionsMap dimensions_map; public: OPENVINO_OP("MLIROp"); - MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types); + MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types, const DimensionsMap& dimensions_map); void validate_and_infer_types() override; NodePtr clone_with_new_inputs(const ov::OutputVector& new_args) const override; bool evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const override; diff --git a/src/common/transformations/src/transformations/mlir/op/matmul.cpp b/src/common/transformations/src/transformations/mlir/op/matmul.cpp new file mode 100644 index 00000000000000..37ac6b1b3e3e9f --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/matmul.cpp @@ -0,0 +1,66 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/Dialect/Linalg/Passes.h" + +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "matmul.hpp" +#include "../convert_common.hpp" + + +namespace { + +using namespace ov::mlir; + +struct ConvertMatMul { + void operator()(ConversionContext& context, NodePtr node) { + auto matmul_node = std::dynamic_pointer_cast(node); + assert(matmul_node); + // FIXME: current code limitation + assert(!matmul_node->get_transpose_a() && matmul_node->get_transpose_b()); + + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + // TODO: Support broadcasts + const auto inputs = context.getInputs(node); + const auto ov_output_element_type = node->get_output_element_type(0); + const auto ov_output_shape = node->get_output_partial_shape(0); + auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); // Instead of this (WRONG): cast(inputs[0].getType()); + + llvm::SmallVector dynamicSizes; + for (auto [idx, dim] : llvm::enumerate(outType.getShape())) { + if (!mlir::ShapedType::isDynamic(dim)) + continue; + // FIXME: correct in case if (!transpose_a && transpose_b) + auto dimSize = builder.create(loc, idx == 0 ? inputs[0] : inputs[1], 1); // TODO: Use symbols instead of taking dims directly from inputs + dynamicSizes.push_back(dimSize); + } + auto empty = builder.create(loc, outType, dynamicSizes); + auto zero = getConstant(builder, ov_output_element_type, 0); + auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); + // TODO: Add other variants of transpose_a/transpose_b + auto matmul = builder.create(loc, mlir::ValueRange{inputs[0], inputs[1]}, mlir::ValueRange{fill.getResult(0)}); + context.addOutputs(node, matmul); + } +}; + +} + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +MatMulPattern::MatMulPattern() : MarkPattern( + wrap_type({any_input(), any_input()}), + ConvertMatMul()) { + } + + +} // namespace mlir +} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/op/matmul.hpp b/src/common/transformations/src/transformations/mlir/op/matmul.hpp new file mode 100644 index 00000000000000..ec326af6cb496f --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/matmul.hpp @@ -0,0 +1,24 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Value.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Builders.h" + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class MatMulPattern : public MarkPattern { +public: + OPENVINO_RTTI("MatMulPattern", "0"); + MatMulPattern(); +}; + + +} // namespace mlir +} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp b/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp index 730f56b402a3a8..1459ae34ef4c07 100644 --- a/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp +++ b/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp @@ -36,8 +36,8 @@ void SubgraphTracker::add_node (NodePtr node, bool belongs) { } if(belongs) { - // Below we refuse to merge subgraphs if all of them cannot merge to a single subgraph, this is rough because - // there are cases when part of the input subgraphs can consume the node and others will come as inputs -- TODO. + // Below we refuse to merge subgraphs if _all_ of them cannot merge to a single subgraph, this is rough because + // there are cases when a _part_ of the input subgraphs can be merged together and consume the new node and other (conflicting) subgraphs will come as inputs -- TODO. // TODO: leave only those input subgraphs that are not conflicting with other subgraphs nor with any dependencies if(input_subgraphs.empty() || intersected(input_subgraphs, input_dependencies)) { // no input subgraphs || cannot merge all due to cycles try_terminate_subgraphs(input_subgraphs, node); From b5370bda3202446b6823c8bfed2767f21c983978 Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Wed, 17 Jul 2024 16:14:52 +0200 Subject: [PATCH 009/121] Allow static input (#141) * Allow eltwise ops with static shapes * Fix matmul output shape --------- Co-authored-by: Sergey Lyalin --- .../transformations/src/transformations/mlir/convert.cpp | 4 ++++ .../transformations/src/transformations/mlir/op/matmul.cpp | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index 339d23053627f9..b978ee314b3667 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -290,6 +290,10 @@ bool elementwise_f32_binary_no_broadcast_predicate(const ov::Output& o if(output_shape[i] != input_shape_a[i] || output_shape[i] != input_shape_b[i]) { return false; } + // Continue if all shapes are static. + if (output_shape[i].is_static() && input_shape_a[i].is_static() && + input_shape_b[i].is_static()) + continue; if(!ov::symbol::are_equal(output_shape[i].get_symbol(), input_shape_a[i].get_symbol()) || !ov::symbol::are_equal(output_shape[i].get_symbol(), input_shape_b[i].get_symbol())) { return false; } diff --git a/src/common/transformations/src/transformations/mlir/op/matmul.cpp b/src/common/transformations/src/transformations/mlir/op/matmul.cpp index 37ac6b1b3e3e9f..f125e40ed409b6 100644 --- a/src/common/transformations/src/transformations/mlir/op/matmul.cpp +++ b/src/common/transformations/src/transformations/mlir/op/matmul.cpp @@ -36,7 +36,10 @@ struct ConvertMatMul { if (!mlir::ShapedType::isDynamic(dim)) continue; // FIXME: correct in case if (!transpose_a && transpose_b) - auto dimSize = builder.create(loc, idx == 0 ? inputs[0] : inputs[1], 1); // TODO: Use symbols instead of taking dims directly from inputs + auto dimSize = + builder.create(loc, + idx == 0 ? inputs[0] : inputs[1], + 0); // TODO: Use symbols instead of taking dims directly from inputs dynamicSizes.push_back(dimSize); } auto empty = builder.create(loc, outType, dynamicSizes); From 1e1d807df2f01e191ed4a42d69341b732e8d36ad Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Fri, 19 Jul 2024 13:39:51 +0200 Subject: [PATCH 010/121] Match and lower ov::Relu (#143) Adds ReLU op matcher and lowering to MLIR named Linalg ops. Also, adds buffer deallocation passes to prevent memory leaks when temporary buffers are created in larger graphs. --- .../src/transformations/mlir/convert.cpp | 62 ++++--------------- .../transformations/mlir/convert_common.cpp | 37 +++++++++++ .../transformations/mlir/convert_common.hpp | 7 +++ .../src/transformations/mlir/mlir_op.cpp | 14 ++++- .../src/transformations/mlir/op/relu.cpp | 59 ++++++++++++++++++ .../src/transformations/mlir/op/relu.hpp | 23 +++++++ 6 files changed, 152 insertions(+), 50 deletions(-) create mode 100644 src/common/transformations/src/transformations/mlir/op/relu.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/relu.hpp diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index b978ee314b3667..a755b033cb6264 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -7,16 +7,18 @@ #include #include #include -#include -#include #include - +#include +#include +#include #include #include #include #include // TODO: Prune unused headers -- it's hard to understand needed ones +#include "conversion_context.hpp" +#include "convert_common.hpp" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Casting.h" #include "llvm/Support/InitLLVM.h" @@ -55,20 +57,16 @@ #include "mlir/Target/LLVMIR/Dialect/All.h" #include "mlir/Target/LLVMIR/Export.h" #include "mlir/Target/LLVMIR/ModuleTranslation.h" +#include "mlir_op.hpp" +#include "op/matmul.hpp" +#include "op/relu.hpp" #include "openvino/core/dimension.hpp" #include "openvino/core/rt_info.hpp" -#include "openvino/pass/pattern/op/wrap_type.hpp" -#include "transformations_visibility.hpp" #include "openvino/core/symbol.hpp" - -#include "transformations/symbolic_transformations/symbolic_optimizations.hpp" - -#include "mlir_op.hpp" -#include "convert_common.hpp" +#include "openvino/pass/pattern/op/wrap_type.hpp" #include "subgraph_tracker.hpp" -#include "conversion_context.hpp" -#include "op/matmul.hpp" - +#include "transformations/symbolic_transformations/symbolic_optimizations.hpp" +#include "transformations_visibility.hpp" namespace { @@ -269,47 +267,12 @@ class Partitioner : public ov::pass::ModelPass { } }; - -bool elementwise_f32_binary_no_broadcast_predicate(const ov::Output& output) { - if(output.get_element_type() != ov::element::f32) { - return false; - } - // Check if implicit broadcast is possible, reject in this case - // Relies on symbolic information -- register SymbolicPropagation before applying this pattern - auto input_shape_a = output.get_node_shared_ptr()->get_input_partial_shape(0); - auto input_shape_b = output.get_node_shared_ptr()->get_input_partial_shape(1); - auto output_shape = output.get_partial_shape(); - if(output_shape.rank().is_dynamic() || input_shape_a.rank().is_dynamic() || input_shape_b.rank().is_dynamic()) { - return false; - } - if(output_shape.rank().get_length() != input_shape_a.rank().get_length() || output_shape.rank().get_length() != input_shape_b.rank().get_length()) { - return false; - } - - for(size_t i = 0; i < output_shape.size(); ++i) { - if(output_shape[i] != input_shape_a[i] || output_shape[i] != input_shape_b[i]) { - return false; - } - // Continue if all shapes are static. - if (output_shape[i].is_static() && input_shape_a[i].is_static() && - input_shape_b[i].is_static()) - continue; - if(!ov::symbol::are_equal(output_shape[i].get_symbol(), input_shape_a[i].get_symbol()) || !ov::symbol::are_equal(output_shape[i].get_symbol(), input_shape_b[i].get_symbol())) { - return false; - } - } - - return true; -} - - template NodePtr elementwise_f32_binary_no_broadcast() { using namespace ov::pass::pattern; - return wrap_type({any_input(), any_input()}, elementwise_f32_binary_no_broadcast_predicate); + return wrap_type({any_input(), any_input()}, elementwise_no_broadcast_predicate); } - void injectMLIR(std::shared_ptr model, MLIRContext* context) { ov::pass::Manager manager; using namespace ov::op; @@ -319,6 +282,7 @@ void injectMLIR(std::shared_ptr model, MLIRContext* context) { manager.register_pass(elementwise_f32_binary_no_broadcast(), ConvertBinary()); manager.register_pass(elementwise_f32_binary_no_broadcast(), ConvertBinary()); manager.register_pass(elementwise_f32_binary_no_broadcast(), ConvertBinary()); + manager.register_pass(); manager.register_pass(); manager.register_pass(context); manager.run_passes(model); diff --git a/src/common/transformations/src/transformations/mlir/convert_common.cpp b/src/common/transformations/src/transformations/mlir/convert_common.cpp index f7c917af1a23cb..6bca04c759a356 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.cpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.cpp @@ -128,5 +128,42 @@ Location createLocation(MLIRContext* ctx, NodePtr node) { return createLayerLocation(ctx, node->get_friendly_name(), node->get_type_name()); } +bool elementwise_no_broadcast_predicate_impl(const ov::Output& output, ov::element::Type type) { + if (output.get_element_type() != type) { + return false; + } + // Check if implicit broadcast is possible, reject in this case + // Relies on symbolic information -- register SymbolicPropagation before applying this pattern + auto inputs = output.get_node_shared_ptr()->inputs(); + auto output_shape = output.get_partial_shape(); + if (output_shape.rank().is_dynamic()) { + return false; + } + if (std::any_of(inputs.begin(), inputs.end(), [&](const ov::Input& input) { + auto input_shape = input.get_partial_shape(); + return input_shape.rank().is_dynamic() || + output_shape.rank().get_length() != input_shape.rank().get_length(); + })) { + return false; + } + + if (std::any_of(inputs.begin(), inputs.end(), [&](const ov::Input& input) { + for (size_t i = 0; i < output_shape.size(); ++i) { + auto input_shape = input.get_partial_shape(); + if (output_shape[i] != input_shape[i]) + return true; + if (output_shape[i].is_static() && input_shape[i].is_static()) + continue; + if (!ov::symbol::are_equal(output_shape[i].get_symbol(), input_shape[i].get_symbol())) + return true; + } + return false; + })) { + return false; + } + + return true; +} + } // namespace mlir } // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/convert_common.hpp b/src/common/transformations/src/transformations/mlir/convert_common.hpp index 7fd19fe875eb80..a33c99e6bedc57 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.hpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.hpp @@ -30,6 +30,13 @@ RankedTensorType importTensor(MLIRContext* ctx, Location createLocation(MLIRContext* ctx, NodePtr node); +bool elementwise_no_broadcast_predicate_impl(const ov::Output& output, ov::element::Type type); + +template +bool elementwise_no_broadcast_predicate(const ov::Output& output) { + return elementwise_no_broadcast_predicate_impl(output, type); +} + // Borrowed it from TPP-MLIR. FIXME: Do we have a better upstreamed alternative? template mlir::arith::ConstantOp getConstant(OpBuilder &builder, const ov::element::Type& precision, T value) { diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 8e7c4e1ce7d9db..51a7a8598f7c88 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -80,12 +80,24 @@ void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module) pm.addPass(bufferization::createEmptyTensorEliminationPass()); pm.addPass(bufferization::createOneShotBufferizePass()); - // TODO: Add deallocation pass/pipeline to avoid memory leaks. + pm.addNestedPass(bufferization::createFinalizingBufferizePass()); // Cleanup after bufferization - possibly remove redundant copies. pm.addNestedPass(createCanonicalizerPass()); pm.addNestedPass(createCSEPass()); + // Deallocation pipeline to avoid memory leaks from created temporary buffers. + pm.addPass(memref::createExpandReallocPass(/*emitDeallocs=*/false)); + pm.addPass(createCanonicalizerPass()); + bufferization::DeallocationOptions deallocOpts; + deallocOpts.privateFuncDynamicOwnership = false; + pm.addPass(bufferization::createOwnershipBasedBufferDeallocationPass(deallocOpts)); + pm.addPass(createCanonicalizerPass()); + pm.addPass(bufferization::createBufferDeallocationSimplificationPass()); + pm.addPass(bufferization::createLowerDeallocationsPass()); + pm.addPass(createCSEPass()); + pm.addPass(createCanonicalizerPass()); + // Blanket-convert any remaining high-level vector ops to loops if any remain. pm.addNestedPass(createConvertVectorToSCFPass()); // pm.addNestedPass(createLinalgGeneralizeNamedOpsPass()); diff --git a/src/common/transformations/src/transformations/mlir/op/relu.cpp b/src/common/transformations/src/transformations/mlir/op/relu.cpp new file mode 100644 index 00000000000000..a25f571f61cddf --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/relu.cpp @@ -0,0 +1,59 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/Dialect/Linalg/Passes.h" + +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "relu.hpp" +#include "../convert_common.hpp" + +namespace { + +using namespace ov::mlir; + +struct ConvertRelu { + void operator()(ConversionContext& context, NodePtr node) { + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + // TODO: Support broadcasts + const auto input = context.getInputs(node)[0]; + const auto ov_output_element_type = node->get_output_element_type(0); + const auto ov_output_shape = node->get_output_partial_shape(0); + auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); + // Named unary ops directly overwrite data in `outs` buffer so, there is no need to provide non-empty + // destination at the tensor-level. + // Use `tensor.empty` to avoid temporary buffer allocation and memcpy after bufferization. + llvm::SmallVector dynamicSizes; + for (auto [idx, dim] : llvm::enumerate(outType.getShape())) { + if (!mlir::ShapedType::isDynamic(dim)) + continue; + auto dimSize = builder.create(loc, input, idx); + dynamicSizes.push_back(dimSize); + } + auto empty = builder.create(loc, outType, dynamicSizes); + auto zero = getConstant(builder, ov_output_element_type, 0); + auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); + auto relu = + builder.create(loc, mlir::ValueRange{input, fill.getResult(0)}, mlir::ValueRange{empty}); + context.addOutputs(node, relu); + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +ReluPattern::ReluPattern() + : MarkPattern(wrap_type({any_input()}, elementwise_no_broadcast_predicate), + ConvertRelu()) {} + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/relu.hpp b/src/common/transformations/src/transformations/mlir/op/relu.hpp new file mode 100644 index 00000000000000..a51c7366d834fb --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/relu.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class ReluPattern : public MarkPattern { +public: + OPENVINO_RTTI("ReluPattern", "0"); + ReluPattern(); +}; + +} // namespace mlir +} // namespace ov From a4dbd987e92caca3098f76b68922b15740e09b6e Mon Sep 17 00:00:00 2001 From: Vladimir Paramuzov Date: Fri, 19 Jul 2024 20:09:34 +0400 Subject: [PATCH 011/121] [GPU] Draft of MLIR and Generic ops (#140) * [GPU] Generic layer draft * mlir op --- .../intel_gpu/plugin/program_builder.hpp | 2 + .../primitives/generic_primitive.hpp | 45 ++++++++++ .../intel_gpu/src/graph/generic_primitive.cpp | 57 +++++++++++++ .../graph/impls/common/generic_primitive.cpp | 80 ++++++++++++++++++ .../src/graph/impls/common/register.cpp | 1 + .../src/graph/impls/common/register.hpp | 1 + .../graph/include/generic_primitive_inst.h | 42 ++++++++++ .../intel_gpu/src/plugin/ops/generic.cpp | 73 ++++++++++++++++ .../intel_gpu/src/plugin/ops/mlir_op.cpp | 83 +++++++++++++++++++ .../intel_gpu/src/plugin/program_builder.cpp | 16 +++- .../src/plugin/transformations_pipeline.cpp | 3 + 11 files changed, 401 insertions(+), 2 deletions(-) create mode 100644 src/plugins/intel_gpu/include/intel_gpu/primitives/generic_primitive.hpp create mode 100644 src/plugins/intel_gpu/src/graph/generic_primitive.cpp create mode 100644 src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp create mode 100644 src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h create mode 100644 src/plugins/intel_gpu/src/plugin/ops/generic.cpp create mode 100644 src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/program_builder.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/program_builder.hpp index 02848734d88830..d37f5d534072a5 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/program_builder.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/program_builder.hpp @@ -174,6 +174,8 @@ class ProgramBuilder final { }; void CreateCustomOp(ProgramBuilder& p, const std::shared_ptr& node, CustomLayerPtr customLayer); +void CreateMLIRSubgraphOp(ProgramBuilder& p, const std::shared_ptr& node); +void CreateGenericOp(ProgramBuilder& p, const std::shared_ptr& node); void CreateUnaryEltwiseOp(ProgramBuilder& p, const std::shared_ptr& node, cldnn::activation_func func, cldnn::activation_additional_params params); void CreateElementwiseOp(ProgramBuilder& p, diff --git a/src/plugins/intel_gpu/include/intel_gpu/primitives/generic_primitive.hpp b/src/plugins/intel_gpu/include/intel_gpu/primitives/generic_primitive.hpp new file mode 100644 index 00000000000000..df01db8d3f65cb --- /dev/null +++ b/src/plugins/intel_gpu/include/intel_gpu/primitives/generic_primitive.hpp @@ -0,0 +1,45 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once +#include "intel_gpu/runtime/layout.hpp" +#include "openvino/core/partial_shape.hpp" +#include "primitive.hpp" +#include "intel_gpu/runtime/memory.hpp" +#include "intel_gpu/runtime/stream.hpp" +#include +#include + +namespace cldnn { + +struct generic_primitive : public primitive_base { + CLDNN_DECLARE_PRIMITIVE(generic_primitive) + + typedef std::function& dependent_events, + cldnn::stream& stream, + const std::vector& inputs, + const std::vector& outputs)> + execute_function; + + typedef std::function(const std::vector& input_shapes)> + shape_infer_function; + + generic_primitive() : primitive_base("", {}) {} + + generic_primitive(const primitive_id& id, + const std::vector& inputs, + const execute_function& execute_f, + const shape_infer_function& shape_infer_f, + size_t num_outputs, + const std::vector& out_types) + : primitive_base(id, {inputs}, num_outputs, out_types), + execute_f(execute_f), + shape_infer_f(shape_infer_f) {} + + const execute_function execute_f; + const shape_infer_function shape_infer_f; +}; + +} // namespace cldnn diff --git a/src/plugins/intel_gpu/src/graph/generic_primitive.cpp b/src/plugins/intel_gpu/src/graph/generic_primitive.cpp new file mode 100644 index 00000000000000..dbd1737f5ab060 --- /dev/null +++ b/src/plugins/intel_gpu/src/graph/generic_primitive.cpp @@ -0,0 +1,57 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "generic_primitive_inst.h" +#include "openvino/core/partial_shape.hpp" +#include "primitive_type_base.h" +#include +#include "json_object.h" +#include + +namespace cldnn { + +primitive_type_id generic_primitive::type_id() { + static primitive_type_base instance; + return &instance; +} + +layout generic_primitive_inst::calc_output_layout(const generic_primitive_node& node, const kernel_impl_params& impl_param) { + return calc_output_layouts(node, impl_param)[0]; +} + +template +std::vector generic_primitive_inst::calc_output_layouts(generic_primitive_node const& /*node*/, const kernel_impl_params& impl_param) { + auto prim = impl_param.typed_desc(); + + std::vector input_shapes; + for (const auto& l : impl_param.input_layouts) { + input_shapes.push_back(l.get()); + } + + std::vector output_shapes = prim->shape_infer_f(input_shapes); + + std::vector out_layouts; + for (size_t i = 0; i < output_shapes.size(); i++) { + out_layouts.emplace_back(output_shapes[i], prim->get_output_data_type(i).value(), format::get_default_format(output_shapes[i].size())); + } + + return out_layouts; +} + +std::string generic_primitive_inst::to_string(generic_primitive_node const& node) { + auto desc = node.get_primitive(); + auto node_info = node.desc_to_json(); + + std::stringstream primitive_description; + + json_composite generic_prim_info; + node_info->add("custom primitive info", generic_prim_info); + node_info->dump(primitive_description); + + return primitive_description.str(); +} + +generic_primitive_inst::typed_primitive_inst(network& network, generic_primitive_node const& node) : parent(network, node) {} + +} // namespace cldnn diff --git a/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp b/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp new file mode 100644 index 00000000000000..035a8ee064d6f9 --- /dev/null +++ b/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp @@ -0,0 +1,80 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "generic_primitive_inst.h" +#include "implementation_map.hpp" +#include "register.hpp" + +#include + +namespace cldnn { +namespace common { + +struct generic_primitive_impl : typed_primitive_impl { + using parent = typed_primitive_impl; + using parent::parent; + + DECLARE_OBJECT_TYPE_SERIALIZATION(cldnn::common::generic_primitive_impl) + + std::unique_ptr clone() const override { + return make_unique(*this); + } + + generic_primitive_impl() : parent() {} + + explicit generic_primitive_impl(const generic_primitive_node& outer) { + set_node_params(outer); + } + + void set_node_params(const program_node& arg) override { + } + + event::ptr execute_impl(const std::vector& events, generic_primitive_inst& instance) override { + std::vector inputs; + inputs.reserve(instance.inputs_memory_count()); + for (size_t i = 0; i < instance.inputs_memory_count(); i++) { + inputs.push_back(instance.input_memory_ptr(i)); + } + + std::vector outputs; + outputs.reserve(instance.outputs_memory_count()); + for (size_t i = 0; i < instance.outputs_memory_count(); i++) { + outputs.push_back(instance.output_memory_ptr(i)); + } + + return instance.node->get_primitive()->execute_f(events, instance.get_network().get_stream(), inputs, outputs); + } + + static std::unique_ptr create(const generic_primitive_node& arg, const kernel_impl_params&) { + return make_unique(arg); + } + + void init_kernels(const kernels_cache& , const kernel_impl_params&) override {} + + void save(BinaryOutputBuffer& ob) const override { + parent::save(ob); + } + + void load(BinaryInputBuffer& ib) override { + parent::load(ib); + } +}; + +namespace detail { + +attach_generic_primitive_common::attach_generic_primitive_common() { + implementation_map::add(impl_types::common, + shape_types::dynamic_shape, + generic_primitive_impl::create, + {}, + {}); + implementation_map::add(impl_types::common, generic_primitive_impl::create, {}); +} + +} // namespace detail +} // namespace common +} // namespace cldnn + +BIND_BINARY_BUFFER_WITH_TYPE(cldnn::common::generic_primitive_impl) +BIND_BINARY_BUFFER_WITH_TYPE(cldnn::generic_primitive) diff --git a/src/plugins/intel_gpu/src/graph/impls/common/register.cpp b/src/plugins/intel_gpu/src/graph/impls/common/register.cpp index aade2ca4812894..16546d495adcf6 100644 --- a/src/plugins/intel_gpu/src/graph/impls/common/register.cpp +++ b/src/plugins/intel_gpu/src/graph/impls/common/register.cpp @@ -13,6 +13,7 @@ namespace common { void register_implementations() { REGISTER_COMMON(condition); REGISTER_COMMON(data); + REGISTER_COMMON(generic_primitive); REGISTER_COMMON(input_layout); REGISTER_COMMON(loop); } diff --git a/src/plugins/intel_gpu/src/graph/impls/common/register.hpp b/src/plugins/intel_gpu/src/graph/impls/common/register.hpp index 109ddf9eb3ed60..8df9f4b6084353 100644 --- a/src/plugins/intel_gpu/src/graph/impls/common/register.hpp +++ b/src/plugins/intel_gpu/src/graph/impls/common/register.hpp @@ -24,6 +24,7 @@ namespace detail { REGISTER_COMMON(condition); REGISTER_COMMON(data); +REGISTER_COMMON(generic_primitive); REGISTER_COMMON(input_layout); REGISTER_COMMON(loop); diff --git a/src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h b/src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h new file mode 100644 index 00000000000000..723e0b3d5abd1e --- /dev/null +++ b/src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h @@ -0,0 +1,42 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "intel_gpu/primitives/generic_primitive.hpp" +#include "primitive_inst.h" + +#include + +namespace cldnn { + +template <> +struct typed_program_node : public typed_program_node_base { + using parent = typed_program_node_base; + +public: + using parent::parent; + + program_node& input(size_t idx = 0) const { return get_dependency(idx); } +}; + +using generic_primitive_node = typed_program_node; + +template <> +class typed_primitive_inst : public typed_primitive_inst_base { + using parent = typed_primitive_inst_base; + using parent::parent; + +public: + template + static std::vector calc_output_layouts(generic_primitive_node const& node, const kernel_impl_params& impl_param); + static layout calc_output_layout(generic_primitive_node const& node, kernel_impl_params const& impl_param); + + static std::string to_string(generic_primitive_node const& node); + + typed_primitive_inst(network& network, generic_primitive_node const& node); +}; + +using generic_primitive_inst = typed_primitive_inst; + +} // namespace cldnn diff --git a/src/plugins/intel_gpu/src/plugin/ops/generic.cpp b/src/plugins/intel_gpu/src/plugin/ops/generic.cpp new file mode 100644 index 00000000000000..a323bc8f8e4bee --- /dev/null +++ b/src/plugins/intel_gpu/src/plugin/ops/generic.cpp @@ -0,0 +1,73 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// +#include "intel_gpu/plugin/common_utils.hpp" +#include "intel_gpu/runtime/internal_properties.hpp" +#include "intel_gpu/runtime/tensor_accessor.hpp" +#include "openvino/core/partial_shape.hpp" +#include "intel_gpu/plugin/program_builder.hpp" +#include "intel_gpu/primitives/generic_primitive.hpp" + +namespace ov { +namespace intel_gpu { + +void CreateGenericOp(ProgramBuilder& p, const std::shared_ptr& op) { + cldnn::generic_primitive::execute_function execute_f = [op]( + const std::vector& dependent_events, + cldnn::stream& stream, + const std::vector& inputs, + const std::vector& outputs) { + // Synchronization as evalute() may be a CPU code + if (stream.get_queue_type() == cldnn::QueueTypes::out_of_order) { + for (auto& ev : dependent_events) { + ev->wait(); + } + } else { + stream.finish(); + } + + cldnn::event::ptr ev = stream.create_user_event(false); + + ov::TensorVector input_host_tensors; + ov::TensorVector output_host_tensors; + + for (size_t i = 0; i < inputs.size(); i++) + input_host_tensors.push_back(make_tensor(inputs[i]->get_layout(), inputs[i]->lock(stream, cldnn::mem_lock_type::read))); + + for (size_t i = 0; i < outputs.size(); i++) + output_host_tensors.push_back(make_tensor(outputs[i]->get_layout(), outputs[i]->lock(stream, cldnn::mem_lock_type::write))); + + OPENVINO_ASSERT(op->evaluate(output_host_tensors, input_host_tensors), + "[GPU] Couldn't execute GenericOp ", op->get_friendly_name()); + + for (size_t i = 0; i < inputs.size(); i++) + inputs[i]->unlock(stream); + + for (size_t i = 0; i < outputs.size(); i++) + outputs[i]->unlock(stream); + + ev->set(); + return ev; + }; + cldnn::generic_primitive::shape_infer_function shape_infer_f = [&op]( + const std::vector& input_shapes) -> std::vector { + // Dummy shape infer + return {input_shapes[0]}; + }; + + auto inputs = p.GetInputInfo(op); + const std::string layerName = layer_type_name_ID(op); + const size_t num_outputs = op->get_output_size(); + + const cldnn::generic_primitive primitive(layerName, + inputs, + execute_f, + shape_infer_f, + num_outputs, + get_output_data_types(op)); + + p.add_primitive(*op, primitive); +} + +} // namespace intel_gpu +} // namespace ov diff --git a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp new file mode 100644 index 00000000000000..49d123bf717449 --- /dev/null +++ b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// +#include "intel_gpu/plugin/common_utils.hpp" +#include "intel_gpu/runtime/internal_properties.hpp" +#include "intel_gpu/runtime/tensor_accessor.hpp" +#include "openvino/core/partial_shape.hpp" +#include "intel_gpu/plugin/program_builder.hpp" +#include "intel_gpu/primitives/generic_primitive.hpp" + +namespace ov { +namespace op { +namespace mlir { +using MLIRSubgraph = ov::op::Op; +} // namespace mlir +} // namespace op +} // namespace ov + +namespace ov { +namespace intel_gpu { + +void CreateMLIRSubgraphOp(ProgramBuilder& p, const std::shared_ptr& op) { + cldnn::generic_primitive::execute_function execute_f = [op]( + const std::vector& dependent_events, + cldnn::stream& stream, + const std::vector& inputs, + const std::vector& outputs) { + // Synchronization as evalute() may be a CPU code + if (stream.get_queue_type() == cldnn::QueueTypes::out_of_order) { + for (auto& ev : dependent_events) { + ev->wait(); + } + } else { + stream.finish(); + } + + cldnn::event::ptr ev = stream.create_user_event(false); + + ov::TensorVector input_host_tensors; + ov::TensorVector output_host_tensors; + + for (size_t i = 0; i < inputs.size(); i++) + input_host_tensors.push_back(make_tensor(inputs[i]->get_layout(), inputs[i]->lock(stream, cldnn::mem_lock_type::read))); + + for (size_t i = 0; i < outputs.size(); i++) + output_host_tensors.push_back(make_tensor(outputs[i]->get_layout(), outputs[i]->lock(stream, cldnn::mem_lock_type::write))); + + OPENVINO_ASSERT(op->evaluate(output_host_tensors, input_host_tensors), + "[GPU] Couldn't execute MLIROp ", op->get_friendly_name()); + + for (size_t i = 0; i < inputs.size(); i++) + inputs[i]->unlock(stream); + + for (size_t i = 0; i < outputs.size(); i++) + outputs[i]->unlock(stream); + + ev->set(); + return ev; + }; + cldnn::generic_primitive::shape_infer_function shape_infer_f = [&op]( + const std::vector& input_shapes) -> std::vector { + // Dummy shape infer + return {input_shapes[0]}; + }; + + auto inputs = p.GetInputInfo(op); + const std::string layerName = layer_type_name_ID(op); + const size_t num_outputs = op->get_output_size(); + + const cldnn::generic_primitive primitive(layerName, + inputs, + execute_f, + shape_infer_f, + num_outputs, + get_output_data_types(op)); + + p.add_primitive(*op, primitive); +} + +REGISTER_FACTORY_IMPL(mlir, MLIRSubgraph); + +} // namespace intel_gpu +} // namespace ov diff --git a/src/plugins/intel_gpu/src/plugin/program_builder.cpp b/src/plugins/intel_gpu/src/plugin/program_builder.cpp index 6646cee1ca3b1c..d78fb5938fdd55 100644 --- a/src/plugins/intel_gpu/src/plugin/program_builder.cpp +++ b/src/plugins/intel_gpu/src/plugin/program_builder.cpp @@ -4,6 +4,7 @@ #include "intel_gpu/runtime/internal_properties.hpp" #include "openvino/core/rt_info/weightless_caching_attributes.hpp" +#include "openvino/core/except.hpp" #include "openvino/op/constant.hpp" #include "openvino/op/split.hpp" #include "openvino/op/variadic_split.hpp" @@ -15,6 +16,7 @@ #include "intel_gpu/plugin/common_utils.hpp" #include "intel_gpu/plugin/program_builder.hpp" #include "intel_gpu/primitives/data.hpp" +#include #include "intel_gpu/runtime/itt.hpp" #include "intel_gpu/runtime/debug_configuration.hpp" #include "intel_gpu/primitives/mutable_data.hpp" @@ -220,9 +222,19 @@ void ProgramBuilder::CreateSingleLayerPrimitive(const std::shared_ptr& } if (!is_created) { - OPENVINO_THROW("Operation: ", op->get_friendly_name(), + std::stringstream ss; + ov::write_all_to_stream(ss, "Operation: ", op->get_friendly_name(), " of type ", op->get_type_name(), - "(", op->get_type_info().version_id, ") is not supported"); + "(", op->get_type_info().version_id, ") is not supported."); + if (op->has_evaluate()) { + std::cout << ss.str() << " Fallback to Op::evaluate()" << std::endl; + // If MLIROp + CreateMLIRSubgraphOp(*this, std::dynamic_pointer_cast(op)); + // else + // CreateGenericOp(*this, op); + } else { + OPENVINO_THROW(ss.str()); + } } } diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index 883603555f9fd4..4b0a5841da7677 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -137,6 +137,7 @@ #include "transformations/init_node_info.hpp" #include "transformations/normalize_l2_decomposition.hpp" #include "transformations/low_precision/mark_dequantization_subgraph.hpp" +#include "transformations/mlir/convert.hpp" #include "transformations/op_conversions/bidirectional_sequences_decomposition.hpp" #include "transformations/op_conversions/convert_batch_to_space.hpp" #include "transformations/op_conversions/convert_broadcast3.hpp" @@ -1547,6 +1548,8 @@ void TransformationsPipeline::apply(std::shared_ptr func) { manager.register_pass(); + ov::pass::transformMLIR(func); + // This is supposed to be the last pass to ensure that we don't have name collisions until // GPU plugin stops using friendly names for program creation manager.register_pass(true); From 6f038fcae8f76ef0ddb31bab4b6f95be9966ef79 Mon Sep 17 00:00:00 2001 From: Sergey Lyalin Date: Mon, 22 Jul 2024 20:16:31 +0400 Subject: [PATCH 012/121] TPP-MLIR as out-of-tree dependency (#142) * Add support for TPP MLIR * Some CMake magic, but not enough for all tools that fail to link * Split enable_tpp_mlir into add_tpp_mlir_includes and add_tpp_mlir_libs. Removed reference of TPP in all places except transformations and main ov library. * Include TPP-MLIR headers * Use MLIR_ALL_LIBS property to link all MLIR libraries * Registering TPP related dialects in injectMLIR * Reference build scripts for MLIR and TPP-MLIR * One more library from tpp as a dependency, reduced includes * Fixed linker problems by properly ordering TPP dependencies * Minor: add more comments --------- Co-authored-by: Renato Golin --- CMakeLists.txt | 9 ++++ cmake/tpp-mlir.cmake | 43 +++++++++++++++++++ scripts/build_mlir.sh | 16 +++++++ scripts/build_tpp_mlir.sh | 14 ++++++ src/cmake/openvino.cmake | 19 ++------ src/common/transformations/CMakeLists.txt | 3 ++ .../src/transformations/mlir/convert.cpp | 26 ++++++++--- .../src/transformations/mlir/mlir_op.cpp | 9 +++- .../tools/compile_tool/CMakeLists.txt | 1 + 9 files changed, 117 insertions(+), 23 deletions(-) create mode 100644 cmake/tpp-mlir.cmake create mode 100755 scripts/build_mlir.sh create mode 100755 scripts/build_tpp_mlir.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index d33dc7122ffd75..4690f6b8976bf2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,6 +141,15 @@ function(ov_developer_package_export_targets) "A list of OpenVINO Developer Package exported targets" FORCE) endfunction() + +# +# TPP-MLIR +# + +# enables tpp-mlir for temporary MLIR lowering CPU pipeline +# FIXME: Move to all-upsteram lowering into XSMM/DNN/MKL +include(cmake/tpp-mlir.cmake) + # # Build # diff --git a/cmake/tpp-mlir.cmake b/cmake/tpp-mlir.cmake new file mode 100644 index 00000000000000..444ba7b546a84c --- /dev/null +++ b/cmake/tpp-mlir.cmake @@ -0,0 +1,43 @@ +# If TPP-MLIR is in library path, add it to the dependencies +# This should be the build directory, not the source or the 'lib' +# FIXME: Make this an actual CMake discovery +if (TPP_MLIR_DIR) + message(STATUS "TPP-MLIR at ${TPP_MLIR_DIR}") + add_compile_definitions(TPP_MLIR) + set(TPP_MLIR_LIBS + # Keep the next two libs at the top of the list to avoid undefined references at link time + TPPPipeline + TPPPassBundles + TPPCheckDialect + TPPCheckToLoops + TPPGPU + TPPIR + TPPLinalgToFunc + TPPLinalgToXSMM + TPPPerfDialect + TPPPerfToFunc + TPPPerfToLoop + TPPRunner + TPPTestLib + TPPTransforms + TPPTransformsUtils + TPPXsmmDialect + TPPXsmmToFunc + xsmm + tpp_xsmm_runner_utils + ) + function(add_tpp_mlir_includes target) + target_include_directories(${target} PRIVATE ${TPP_MLIR_DIR}/../include ${TPP_MLIR_DIR}/include) + endfunction() + function(add_tpp_mlir_libs target) + target_link_directories(${target} PRIVATE ${TPP_MLIR_DIR}/lib) + target_link_libraries(${target} PRIVATE ${TPP_MLIR_LIBS}) + endfunction() +else() + function(add_tpp_mlir_includes target) + message(DEBUG "TPP-MLIR not enabled, skipping ${target}") + endfunction() + function(add_tpp_mlir_libs target) + message(DEBUG "TPP-MLIR not enabled, skipping ${target}") + endfunction() +endif() diff --git a/scripts/build_mlir.sh b/scripts/build_mlir.sh new file mode 100755 index 00000000000000..4315efd0ca3758 --- /dev/null +++ b/scripts/build_mlir.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Run it in llvm-project/build directory + +cmake -G Ninja ../llvm \ + -DLLVM_ENABLE_PROJECTS="mlir" \ + -DLLVM_BUILD_EXAMPLES=ON \ + -DLLVM_INSTALL_UTILS=ON \ + -DLLVM_TARGETS_TO_BUILD="host" \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DLLVM_ENABLE_ASSERTIONS=ON \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DLLVM_USE_LINKER=lld + +ninja \ No newline at end of file diff --git a/scripts/build_tpp_mlir.sh b/scripts/build_tpp_mlir.sh new file mode 100755 index 00000000000000..52e5d6b05d1181 --- /dev/null +++ b/scripts/build_tpp_mlir.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Run it in tpp-mlir/build subdirectory +# Set CUSTOM_LLVM_ROOT to llvm-project/build directory + +cmake -G Ninja .. \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DMLIR_DIR=$CUSTOM_LLVM_ROOT/lib/cmake/mlir \ + -DLLVM_EXTERNAL_LIT=$CUSTOM_LLVM_ROOT/bin/llvm-lit \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DLLVM_USE_LINKER=lld + +cmake --build . --target check-tpp \ No newline at end of file diff --git a/src/cmake/openvino.cmake b/src/cmake/openvino.cmake index 4904eb6b2c66d2..348087d5d3c320 100644 --- a/src/cmake/openvino.cmake +++ b/src/cmake/openvino.cmake @@ -51,22 +51,7 @@ target_include_directories(${TARGET_NAME} INTERFACE find_package(MLIR REQUIRED CONFIG) -set(MLIR_OPENVINO_LIBS - MLIRAnalysis - MLIRExecutionEngine - MLIRIR - MLIRJitRunner - MLIRLLVMDialect - MLIRLLVMToLLVMIRTranslation - MLIRToLLVMIRTranslationRegistration - MLIRParser - MLIRTargetLLVMIRExport - MLIRSupport - MLIROptLib - LLVMX86AsmParser - MLIRFuncDialect - MLIRFuncAllExtensions - MLIRUBToLLVM) +get_property(MLIR_ALL_LIBS GLOBAL PROPERTY MLIR_ALL_LIBS) target_link_libraries(${TARGET_NAME} PRIVATE openvino::reference @@ -78,6 +63,8 @@ target_link_libraries(${TARGET_NAME} PUBLIC $<$,$,9.1>>:stdc++fs> $<$,$,9.0>>:c++fs>) +add_tpp_mlir_libs(${TARGET_NAME}) + if (TBBBIND_2_5_FOUND) target_link_libraries(${TARGET_NAME} PRIVATE ${TBBBIND_2_5_IMPORTED_TARGETS}) endif() diff --git a/src/common/transformations/CMakeLists.txt b/src/common/transformations/CMakeLists.txt index 3d177f4c10eda0..9eff9864663570 100644 --- a/src/common/transformations/CMakeLists.txt +++ b/src/common/transformations/CMakeLists.txt @@ -35,6 +35,9 @@ target_include_directories(${TARGET_NAME}_obj PRIVATE "${PUBLIC_HEADERS_DIR}" "${MLIR_INCLUDE_DIRS}" "${LLVM_INCLUDE_DIRS}") +add_tpp_mlir_includes(${TARGET_NAME}_obj) + + ov_add_clang_format_target(${TARGET_NAME}_clang FOR_TARGETS ${TARGET_NAME}_obj) ov_mark_target_as_cc(${TARGET_NAME}_obj) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index a755b033cb6264..af3f7ca1cac227 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -57,6 +57,14 @@ #include "mlir/Target/LLVMIR/Dialect/All.h" #include "mlir/Target/LLVMIR/Export.h" #include "mlir/Target/LLVMIR/ModuleTranslation.h" + +#ifdef TPP_MLIR // If TPP is available +#include "TPP/Dialect/Check/CheckDialect.h" +#include "TPP/Dialect/Perf/PerfDialect.h" +#include "TPP/Dialect/Xsmm/XsmmDialect.h" +#include "TPP/GPU/Utils.h" +#endif + #include "mlir_op.hpp" #include "op/matmul.hpp" #include "op/relu.hpp" @@ -302,16 +310,24 @@ MLIRContext* get_shared_mlir_context() { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); - // Initialize GPU-related LLVM machinery - // tpp::initializeGpuTargets(); + std::cerr << "[ DEBUG ] Using TPP_MLIR: "; + #if TPP_MLIR + // Initialize GPU-related LLVM machinery + tpp::initializeGpuTargets(); + std::cerr << "YES\n"; + #else + std::cerr << "NO\n"; + #endif // Add the following to include *all* MLIR Core dialects, or selectively // include what you need like above. You only need to register dialects that // will be *parsed* by the tool, not the one generated DialectRegistry registry; - // registry.insert(); - // registry.insert(); - // registry.insert(); + #if TPP_MLIR + registry.insert(); + registry.insert(); + registry.insert(); + #endif registerAllDialects(registry); registerAllExtensions(registry); diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 51a7a8598f7c88..eea1019f60db91 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -53,6 +53,11 @@ #include "mlir/Target/LLVMIR/Export.h" #include "mlir/Target/LLVMIR/ModuleTranslation.h" +#ifdef TPP_MLIR // If TPP is available +#include "TPP/PassBundles.h" +#include "TPP/Passes.h" +#endif + namespace { using namespace mlir; @@ -64,9 +69,9 @@ void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module) // A set of default passes that lower any input IR to LLVM PassManager pm(module->getContext()); -#if 0 // TODO: if TPP is available +#if TPP_MLIR - tpp::DefaultPipelineOptions defPipelineOpts{defGpuBackend}; + tpp::DefaultPipelineOptions defPipelineOpts; pm.addPass(tpp::createDefaultPipeline(defPipelineOpts)); #else // Simplified default lowering to LLVM from LLVM tests diff --git a/src/plugins/intel_npu/tools/compile_tool/CMakeLists.txt b/src/plugins/intel_npu/tools/compile_tool/CMakeLists.txt index 8658ef2f9da13f..719ccc4f372e45 100644 --- a/src/plugins/intel_npu/tools/compile_tool/CMakeLists.txt +++ b/src/plugins/intel_npu/tools/compile_tool/CMakeLists.txt @@ -39,6 +39,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR OV_COMPILER_IS_CLANG) ov_add_compiler_flags(-Wno-missing-declarations) endif() + # # Install # From ff4d0187070821eb97ff869b48479126ef0d9e98 Mon Sep 17 00:00:00 2001 From: Sergey Lyalin Date: Mon, 22 Jul 2024 20:27:39 +0400 Subject: [PATCH 013/121] Fixed double free due to missing return statement in Partitioner::run_on_model (#145) --- src/common/transformations/src/transformations/mlir/convert.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index af3f7ca1cac227..ee40c112b40e9a 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -272,6 +272,7 @@ class Partitioner : public ov::pass::ModelPass { tracker.add_node(node, get_subgraph_mark(node)); } tracker.finalize(); + return true; } }; From 863f139e396120cf21e6115c25feb262ac7d3da2 Mon Sep 17 00:00:00 2001 From: Sergey Lyalin Date: Tue, 23 Jul 2024 16:57:45 +0400 Subject: [PATCH 014/121] Fixing xsmm runner dynamic load (#146) * Postponed of tpp_xsmm_runner_utils load * Added xsmm runner libs copying to the target ov direcotry listing the names of libs explicitly (FIXME) --- cmake/tpp-mlir.cmake | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cmake/tpp-mlir.cmake b/cmake/tpp-mlir.cmake index 444ba7b546a84c..300ac99df3b734 100644 --- a/cmake/tpp-mlir.cmake +++ b/cmake/tpp-mlir.cmake @@ -32,6 +32,14 @@ if (TPP_MLIR_DIR) function(add_tpp_mlir_libs target) target_link_directories(${target} PRIVATE ${TPP_MLIR_DIR}/lib) target_link_libraries(${target} PRIVATE ${TPP_MLIR_LIBS}) + target_link_options(${target} PRIVATE + -Wl,--no-as-needed + -L${TPP_MLIR_DIR}/lib + -ltpp_xsmm_runner_utils + -Wl,--as-needed + ) + #FIXME: Provide platform-independent way of doing that: + install(FILES ${TPP_MLIR_DIR}/lib/libtpp_xsmm_runner_utils.so ${TPP_MLIR_DIR}/lib/libtpp_xsmm_runner_utils.so.19.0git DESTINATION ${OV_CPACK_RUNTIMEDIR}) endfunction() else() function(add_tpp_mlir_includes target) From d44f7c905087db0306625abe160a2087084997aa Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Thu, 25 Jul 2024 12:13:12 +0200 Subject: [PATCH 015/121] Pass XSMM runner lib to MLIR execution engine (#147) Manually finds and passes XSMM runner library to the MLIR JIT engine to resolve missing TPP xsmm_* symbols when executing from Python. Works only for Linux currently. --- cmake/tpp-mlir.cmake | 6 +++- .../src/transformations/mlir/mlir_op.cpp | 30 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/cmake/tpp-mlir.cmake b/cmake/tpp-mlir.cmake index 300ac99df3b734..5c658f7c6bd67e 100644 --- a/cmake/tpp-mlir.cmake +++ b/cmake/tpp-mlir.cmake @@ -27,7 +27,11 @@ if (TPP_MLIR_DIR) tpp_xsmm_runner_utils ) function(add_tpp_mlir_includes target) - target_include_directories(${target} PRIVATE ${TPP_MLIR_DIR}/../include ${TPP_MLIR_DIR}/include) + target_include_directories(${target} + PRIVATE + ${TPP_MLIR_DIR}/../include + ${TPP_MLIR_DIR}/include + ${TPP_MLIR_DIR}/../runtime) endfunction() function(add_tpp_mlir_libs target) target_link_directories(${target} PRIVATE ${TPP_MLIR_DIR}/lib) diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index eea1019f60db91..7bba79c2dffcec 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -4,16 +4,17 @@ #include "mlir_op.hpp" -#include #include #include #include #include +#include #include "mlir/Dialect/Bufferization/Transforms/Passes.h" #include "mlir/Pass/PassManager.h" // TODO: Prune unused headers -- it's hard to understand needed ones +#include "llvm/ADT/SmallVector.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Casting.h" #include "llvm/Support/InitLLVM.h" @@ -54,8 +55,16 @@ #include "mlir/Target/LLVMIR/ModuleTranslation.h" #ifdef TPP_MLIR // If TPP is available -#include "TPP/PassBundles.h" -#include "TPP/Passes.h" +# if defined(__APPLE__) || defined(__linux__) || defined(__EMSCRIPTEN__) +# include +# else +# error "Unsupported OS" +# endif + +# include "PerfRunnerUtils.h" +# include "TPP/PassBundles.h" +# include "TPP/Passes.h" +# include "openvino/util/file_util.hpp" #endif namespace { @@ -255,6 +264,19 @@ namespace mlir { using namespace ::mlir; +static std::string get_xsmm_runner_path() { +#if defined(__APPLE__) || defined(__linux__) || defined(__EMSCRIPTEN__) + // TODO: Add multiplatform shared library search support. + Dl_info info; + if (dladdr(reinterpret_cast(perf_start_timer), &info) == 0) { + llvm::errs() << "failed to find XSMM Runner library\n"; + abort(); + } + return ov::util::get_absolute_file_path(info.dli_fname); +#else +# error "Unsupported OS" +#endif +} MLIREvaluate::MLIREvaluate(OwningOpRef _module) : module(std::move(_module)) { if (true) { @@ -281,6 +303,8 @@ MLIREvaluate::MLIREvaluate(OwningOpRef _module) : module(std::mo engineOptions.transformer = optPipeline; // opt level looks to be overriden in lowerToLLVMIR, but is still used // in `create` independently engineOptions.llvmModuleBuilder = lowerToLLVMIR; + std::string xsmmLibPath = get_xsmm_runner_path(); + engineOptions.sharedLibPaths = SmallVector{xsmmLibPath}; auto maybeEngine = mlir::ExecutionEngine::create(module.get(), engineOptions); if (maybeEngine) { engine = std::move(maybeEngine.get()); From c72dc8a1542cc9d1119853ddc5b7a80bf11ad22a Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Thu, 25 Jul 2024 16:46:40 +0200 Subject: [PATCH 016/121] Revert "Pass XSMM runner lib to MLIR execution engine (#147)" (#149) This reverts commit a510e017e1c2f61cb04b9916fa3705d0f4b15af2. --- cmake/tpp-mlir.cmake | 6 +--- .../src/transformations/mlir/mlir_op.cpp | 30 ++----------------- 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/cmake/tpp-mlir.cmake b/cmake/tpp-mlir.cmake index 5c658f7c6bd67e..300ac99df3b734 100644 --- a/cmake/tpp-mlir.cmake +++ b/cmake/tpp-mlir.cmake @@ -27,11 +27,7 @@ if (TPP_MLIR_DIR) tpp_xsmm_runner_utils ) function(add_tpp_mlir_includes target) - target_include_directories(${target} - PRIVATE - ${TPP_MLIR_DIR}/../include - ${TPP_MLIR_DIR}/include - ${TPP_MLIR_DIR}/../runtime) + target_include_directories(${target} PRIVATE ${TPP_MLIR_DIR}/../include ${TPP_MLIR_DIR}/include) endfunction() function(add_tpp_mlir_libs target) target_link_directories(${target} PRIVATE ${TPP_MLIR_DIR}/lib) diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 7bba79c2dffcec..eea1019f60db91 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -4,17 +4,16 @@ #include "mlir_op.hpp" +#include #include #include #include #include -#include #include "mlir/Dialect/Bufferization/Transforms/Passes.h" #include "mlir/Pass/PassManager.h" // TODO: Prune unused headers -- it's hard to understand needed ones -#include "llvm/ADT/SmallVector.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Casting.h" #include "llvm/Support/InitLLVM.h" @@ -55,16 +54,8 @@ #include "mlir/Target/LLVMIR/ModuleTranslation.h" #ifdef TPP_MLIR // If TPP is available -# if defined(__APPLE__) || defined(__linux__) || defined(__EMSCRIPTEN__) -# include -# else -# error "Unsupported OS" -# endif - -# include "PerfRunnerUtils.h" -# include "TPP/PassBundles.h" -# include "TPP/Passes.h" -# include "openvino/util/file_util.hpp" +#include "TPP/PassBundles.h" +#include "TPP/Passes.h" #endif namespace { @@ -264,19 +255,6 @@ namespace mlir { using namespace ::mlir; -static std::string get_xsmm_runner_path() { -#if defined(__APPLE__) || defined(__linux__) || defined(__EMSCRIPTEN__) - // TODO: Add multiplatform shared library search support. - Dl_info info; - if (dladdr(reinterpret_cast(perf_start_timer), &info) == 0) { - llvm::errs() << "failed to find XSMM Runner library\n"; - abort(); - } - return ov::util::get_absolute_file_path(info.dli_fname); -#else -# error "Unsupported OS" -#endif -} MLIREvaluate::MLIREvaluate(OwningOpRef _module) : module(std::move(_module)) { if (true) { @@ -303,8 +281,6 @@ MLIREvaluate::MLIREvaluate(OwningOpRef _module) : module(std::mo engineOptions.transformer = optPipeline; // opt level looks to be overriden in lowerToLLVMIR, but is still used // in `create` independently engineOptions.llvmModuleBuilder = lowerToLLVMIR; - std::string xsmmLibPath = get_xsmm_runner_path(); - engineOptions.sharedLibPaths = SmallVector{xsmmLibPath}; auto maybeEngine = mlir::ExecutionEngine::create(module.get(), engineOptions); if (maybeEngine) { engine = std::move(maybeEngine.get()); From 3707523b3e5b2a6398b5bc82d48622a5634b9374 Mon Sep 17 00:00:00 2001 From: Sergey Lyalin Date: Thu, 25 Jul 2024 20:13:03 +0400 Subject: [PATCH 017/121] Broadcast support for elementwise ops (#148) * Broadcast support for element-wise ops and more economical way of dynamic dimensions handling based on symbols. * Simpler broadcast dims cacluations, moved to common utils. * Use common function to compute dynamic dimension values in MatMul and Relu. * Element type configurable restriction for the new BinaryEltwisePattern. Forced f32 in the conversion pipeline. --- .../mlir/conversion_context.cpp | 19 +++ .../mlir/conversion_context.hpp | 7 + .../src/transformations/mlir/convert.cpp | 56 ++++---- .../transformations/mlir/convert_common.cpp | 120 +++++++++++++++--- .../transformations/mlir/convert_common.hpp | 14 ++ .../mlir/op/binary_eltwise.cpp | 91 +++++++++++++ .../mlir/op/binary_eltwise.hpp | 42 ++++++ .../src/transformations/mlir/op/matmul.cpp | 18 +-- .../src/transformations/mlir/op/relu.cpp | 14 +- 9 files changed, 307 insertions(+), 74 deletions(-) create mode 100644 src/common/transformations/src/transformations/mlir/op/binary_eltwise.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/binary_eltwise.hpp diff --git a/src/common/transformations/src/transformations/mlir/conversion_context.cpp b/src/common/transformations/src/transformations/mlir/conversion_context.cpp index 5d1dfbf619a9b0..89692ae2bc80d7 100644 --- a/src/common/transformations/src/transformations/mlir/conversion_context.cpp +++ b/src/common/transformations/src/transformations/mlir/conversion_context.cpp @@ -58,6 +58,25 @@ void ConversionContext::set_convertor(NodePtr node, const Convertor& convertor) node->get_rt_info()[rt_info_convertor()] = as_any; } +Value ConversionContext::get_dimension_value(const Dimension& d) { + auto symbol = d.get_symbol(); + assert(symbol); + symbol = ov::symbol::ancestor_of(symbol); + // Suppose all dimensions are known and the map is populated + // FIXME: Add dimensions on demand to avoid unnecessary operations in the produced MLIR + assert(dimension_map.count(symbol)); + return dimension_map.at(symbol); +} + +SmallVector ConversionContext::get_dynamic_dimension_values (const PartialShape& shape) { + SmallVector dims; + for (const auto& dim: shape) { + if (dim.is_dynamic()) { + dims.push_back(get_dimension_value(dim)); + } + } + return dims; +} const std::string& subgraph_mark() { diff --git a/src/common/transformations/src/transformations/mlir/conversion_context.hpp b/src/common/transformations/src/transformations/mlir/conversion_context.hpp index 3f2c0ee9b34619..314b0529642453 100644 --- a/src/common/transformations/src/transformations/mlir/conversion_context.hpp +++ b/src/common/transformations/src/transformations/mlir/conversion_context.hpp @@ -11,6 +11,7 @@ #include "mlir/IR/Builders.h" #include "typedefs.hpp" +#include "convert_common.hpp" namespace ov { namespace mlir { @@ -20,6 +21,7 @@ using ::mlir::MLIRContext; using ::mlir::OpBuilder; using ::mlir::Operation; using ::mlir::SmallVector; +using ::mlir::ValueRange; class ConversionContext { static std::string rt_info_convertor (); @@ -32,6 +34,7 @@ class ConversionContext { mlir::MLIRContext* context; mlir::OpBuilder* block_builder; NodeOutputMap nodeOutputMap; + std::map dimension_map; ConversionContext(mlir::MLIRContext* context, mlir::OpBuilder* block_builder); @@ -45,6 +48,10 @@ class ConversionContext { static void set_convertor(NodePtr node, const Convertor& convertor); void convert(NodePtr node); + + Value get_dimension_value(const Dimension& d); + + SmallVector get_dynamic_dimension_values (const PartialShape& shape); }; diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index ee40c112b40e9a..63bafdc0d5fa1a 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -68,6 +68,7 @@ #include "mlir_op.hpp" #include "op/matmul.hpp" #include "op/relu.hpp" +#include "op/binary_eltwise.hpp" #include "openvino/core/dimension.hpp" #include "openvino/core/rt_info.hpp" #include "openvino/core/symbol.hpp" @@ -107,30 +108,6 @@ SmallVector get_types_for_values(mlir::MLIRContext* context, const o return types; } -template -struct ConvertBinary { - void operator()(ConversionContext& context, NodePtr node) { - auto loc = createLocation(context.context, node); - auto& builder = context.builder(); - // TODO: Support broadcasts - const auto inputs = context.getInputs(node); - auto outType = cast(inputs[0].getType()); - // Named binary ops directly overwrite data in `outs` buffer so, there is no need to provide non-empty - // destination at the tensor-level. - // Use `tensor.empty` to avoid temporary buffer allocation and memcpy after bufferization. - llvm::SmallVector dynamicSizes; - for (auto [idx, dim] : llvm::enumerate(outType.getShape())) { - if (!mlir::ShapedType::isDynamic(dim)) - continue; - auto dimSize = builder.create(loc, inputs[0], idx); - dynamicSizes.push_back(dimSize); - } - auto empty = builder.create(loc, outType, dynamicSizes); - auto op = builder.create(loc, mlir::ValueRange{inputs[0], inputs[1]}, mlir::ValueRange{empty}); - context.addOutputs(node, op); - } -}; - mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, const ov::OutputVector& inputs, @@ -159,6 +136,24 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, auto loc = createLocation(context, inputs[i].get_node_shared_ptr()); auto tensor = block_builder.create(loc, funcInputVal, /*restrict = */ true); conversion_context.nodeOutputMap.emplace(inputs[i], tensor); + + // FIXME: Avoid pre-population of dimension_map, take dimension values only if needed + auto input_shape = inputs[i].get_partial_shape(); + auto input_rank = input_shape.rank(); + if(input_rank.is_static()) { + for(size_t j = 0; j < input_rank.get_length(); ++j) { + auto dim = input_shape[j]; + if(dim.is_dynamic()) { + auto symbol = dim.get_symbol(); + assert(symbol); + symbol = ov::symbol::ancestor_of(symbol); + if(dim.is_dynamic() && !conversion_context.dimension_map.count(symbol)) { + auto dimSize = block_builder.create(loc, tensor, j); + conversion_context.dimension_map[symbol] = dimSize; + } + } + } + } } for (size_t i = 0; i < nodes.size(); ++i) { @@ -276,21 +271,16 @@ class Partitioner : public ov::pass::ModelPass { } }; -template -NodePtr elementwise_f32_binary_no_broadcast() { - using namespace ov::pass::pattern; - return wrap_type({any_input(), any_input()}, elementwise_no_broadcast_predicate); -} void injectMLIR(std::shared_ptr model, MLIRContext* context) { ov::pass::Manager manager; using namespace ov::op; manager.set_per_pass_validation(false); manager.register_pass(); - manager.register_pass(elementwise_f32_binary_no_broadcast(), ConvertBinary()); - manager.register_pass(elementwise_f32_binary_no_broadcast(), ConvertBinary()); - manager.register_pass(elementwise_f32_binary_no_broadcast(), ConvertBinary()); - manager.register_pass(elementwise_f32_binary_no_broadcast(), ConvertBinary()); + manager.register_pass>(ov::element::f32); + manager.register_pass>(ov::element::f32); + manager.register_pass>(ov::element::f32); + manager.register_pass>(ov::element::f32); manager.register_pass(); manager.register_pass(); manager.register_pass(context); diff --git a/src/common/transformations/src/transformations/mlir/convert_common.cpp b/src/common/transformations/src/transformations/mlir/convert_common.cpp index 6bca04c759a356..1499acb0ba844f 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.cpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.cpp @@ -132,38 +132,128 @@ bool elementwise_no_broadcast_predicate_impl(const ov::Output& output, if (output.get_element_type() != type) { return false; } + if (has_dynamic_rank(output.get_node_shared_ptr())) { + return false; + } // Check if implicit broadcast is possible, reject in this case // Relies on symbolic information -- register SymbolicPropagation before applying this pattern auto inputs = output.get_node_shared_ptr()->inputs(); auto output_shape = output.get_partial_shape(); - if (output_shape.rank().is_dynamic()) { - return false; - } + if (std::any_of(inputs.begin(), inputs.end(), [&](const ov::Input& input) { auto input_shape = input.get_partial_shape(); - return input_shape.rank().is_dynamic() || - output_shape.rank().get_length() != input_shape.rank().get_length(); + if(output_shape.rank().get_length() != input_shape.rank().get_length()) { + return true; + } + for (size_t i = 0; i < output_shape.size(); ++i) { + if(!are_equal_dimensions(input_shape[i], output_shape[i])) + return true; + } + return false; })) { return false; } + return true; +} + +bool has_dynamic_rank(NodePtr node) { + auto inputs = node->inputs(); + auto outputs = node->outputs(); if (std::any_of(inputs.begin(), inputs.end(), [&](const ov::Input& input) { - for (size_t i = 0; i < output_shape.size(); ++i) { - auto input_shape = input.get_partial_shape(); - if (output_shape[i] != input_shape[i]) - return true; - if (output_shape[i].is_static() && input_shape[i].is_static()) - continue; - if (!ov::symbol::are_equal(output_shape[i].get_symbol(), input_shape[i].get_symbol())) - return true; - } - return false; + return input.get_partial_shape().rank().is_dynamic(); })) { + return true; + } + if (std::any_of(outputs.begin(), outputs.end(), [&](const ov::Output& output) { + return output.get_partial_shape().rank().is_dynamic(); + })) { + return true; + } + return false; +} + +bool are_equal_dimensions(Dimension d1, Dimension d2) { + return + d1.is_static() && d2.is_static() && d1 == d2 + || + ov::symbol::are_equal(d1.get_symbol(), d2.get_symbol()); +} + +bool has_broadcast(Dimension from, Dimension to) { + return from.is_static() && from.get_length() == 1 && !are_equal_dimensions(from, to); +} + +bool statically_broadcastable(const PartialShape& from, const PartialShape& to) { + if(from.rank().is_dynamic() || to.rank().is_dynamic()) { // FIXME: `from` can has dynamic rank + return false; + } + + auto from_rank = from.rank().get_length(); + auto to_rank = to.rank().get_length(); + + if(from_rank > to_rank) { // such cases shouldn't be allowed to this function, but kept to make the function generic return false; } + auto offset = to_rank - from_rank; + for(size_t i = 0; i < from_rank; ++i) { + auto d_from = from[i]; + auto d_to = to[offset + i]; + if(!are_equal_dimensions(d_from, d_to) && !has_broadcast(d_from, d_to)) { + // cannot deduce neither dimensions broadcast nor dimensions equality + return false; + } + } + return true; } +BroadcastDimensions broadcast_dimensions(const PartialShape& src, const PartialShape& dst) { + assert(statically_broadcastable(src, dst)); + + auto src_rank = src.rank().get_length(); + auto dst_rank = dst.rank().get_length(); + auto offset = dst_rank - src_rank; + + BroadcastDimensions result; + auto& [collapse_groups, dimensions] = result; + ReassociationIndices group; + bool group_bonded = false; // true if `group` has a non-brodcasted dimension + + size_t dst_i = 0; // dimension index in the `dst` shape + for(; dst_i < offset; ++dst_i) { + dimensions.push_back(dst_i); + } + for(; dst_i < dst_rank; ++dst_i) { + auto src_i = dst_i - offset; + auto src_d = src[src_i]; + auto dst_d = dst[dst_i]; + if(has_broadcast(src_d, dst_d)) { + dimensions.push_back(dst_i); + } else { + if(group_bonded) { + collapse_groups.emplace_back(group); + group = ReassociationIndices(); + } else { + group_bonded = true; + } + } + group.push_back(src_i); + } + + if(group_bonded && !group.empty()) { + collapse_groups.emplace_back(group); + } + + assert(dst_rank - dimensions.size() == collapse_groups.size()); + + return result; +} + +bool symbol_ancestor_less (SymbolPtr x, SymbolPtr y) { + return ov::symbol::ancestor_of(x) < ov::symbol::ancestor_of(y); +} + } // namespace mlir } // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/convert_common.hpp b/src/common/transformations/src/transformations/mlir/convert_common.hpp index a33c99e6bedc57..6622ea5ed70c0c 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.hpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.hpp @@ -9,6 +9,7 @@ #include "mlir/IR/MLIRContext.h" #include "mlir/IR/Location.h" #include "mlir/Dialect/Arith/IR/Arith.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" #include "typedefs.hpp" @@ -52,5 +53,18 @@ mlir::arith::ConstantOp getConstant(OpBuilder &builder, const ov::element::Type& return builder.create(unkLoc, type, attr); } +bool has_dynamic_rank(NodePtr node); + +bool are_equal_dimensions(Dimension d1, Dimension d2); + +bool has_broadcast(Dimension from, Dimension to); + +bool statically_broadcastable(const PartialShape& from, const PartialShape& to); + +using BroadcastDimensions = std::tuple, SmallVector>; +BroadcastDimensions broadcast_dimensions(const PartialShape& from, const PartialShape& to); + +bool symbol_ancestor_less (SymbolPtr x, SymbolPtr y); + } // namespace mlir } // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/op/binary_eltwise.cpp b/src/common/transformations/src/transformations/mlir/op/binary_eltwise.cpp new file mode 100644 index 00000000000000..c840c720b4f497 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/binary_eltwise.cpp @@ -0,0 +1,91 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/Dialect/Linalg/Passes.h" + +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "binary_eltwise.hpp" + +namespace { + +using namespace ov; +using namespace ov::mlir; +using ::mlir::ValueRange; + +class ConvertBinaryEltwise { + + BinaryEltwisePatternBase::Builder m_op_builder; + +public: + + ConvertBinaryEltwise(BinaryEltwisePatternBase::Builder op_builder) : m_op_builder(op_builder) {} + + void operator()(ConversionContext& context, NodePtr node) { + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto inputs = context.getInputs(node); + const auto ov_output_element_type = node->get_output_element_type(0); + const auto ov_output_shape = node->get_output_partial_shape(0); + auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); + const int output_rank = ov_output_shape.rank().get_length(); + + SmallVector dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); + + SmallVector broadcasted_inputs; + for(size_t i = 0; i < inputs.size(); ++i) { + auto [collapse_groups, dimensions] = broadcast_dimensions(node->get_input_partial_shape(i), ov_output_shape); + if(!dimensions.empty()) { + // FIXME: Find a way to avoid dimension squeezing before applying linalg.broadcast + // Step 1: Squeeze input shape to eliminate broadcasted dimensions + auto squeezed = builder.create(loc, inputs[i], collapse_groups); + // Step 2: Broadcast squeezed shape to the target shape + auto empty = builder.create(loc, outType, dynamic_dimensions); + auto op = builder.create(loc, squeezed, empty, dimensions); + broadcasted_inputs.push_back(op.getResult()[0]); + } else { + broadcasted_inputs.push_back(inputs[i]); + } + } + + auto empty = builder.create(loc, outType, dynamic_dimensions); + auto op = m_op_builder(builder, loc, ValueRange(broadcasted_inputs), ValueRange{empty}); + context.addOutputs(node, op); + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; + +BinaryEltwisePatternBase::BinaryEltwisePatternBase(NodeTypeInfo wrapped_type, Builder op_builder, const std::set& element_types) + : MarkPattern( + std::make_shared( + wrapped_type, + [element_types](const Output& output) { + if(!element_types.empty() && !element_types.count(output.get_element_type())) { + return false; + } + auto node = output.get_node_shared_ptr(); + for(const auto& input: node->inputs()) { + if(!statically_broadcastable(input.get_partial_shape(), output.get_partial_shape())) { + return false; + } + } + return true; + }, + OutputVector{any_input(), any_input()}), + ConvertBinaryEltwise(op_builder)) + {} + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/binary_eltwise.hpp b/src/common/transformations/src/transformations/mlir/op/binary_eltwise.hpp new file mode 100644 index 00000000000000..1c410608cbc4a5 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/binary_eltwise.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class BinaryEltwisePatternBase : public MarkPattern { +public: + using Builder = std::function; + + OPENVINO_RTTI("BinaryEltwisePatternBase", "0"); + BinaryEltwisePatternBase(NodeTypeInfo wrapped_type, Builder op_builder, const std::set& element_types = {}); +}; + + +template +class BinaryEltwisePattern : public BinaryEltwisePatternBase { +public: + // Allow conversion for given `element_types` only, except case when `element_types` is empty which means no restrictions on types, everything is allowed. + BinaryEltwisePattern (const std::set& element_types = {}) : + BinaryEltwisePatternBase( + OVOp::get_type_info_static(), + [](OpBuilder& builder, ::mlir::Location loc, ValueRange ins, ValueRange outs) -> Operation* { + return builder.create(loc, ins, outs); + }, + element_types) + {} + + BinaryEltwisePattern (const element::Type& element_type) : + BinaryEltwisePattern(std::set{element_type}) + {} +}; + + +} // namespace mlir +} // namespace ov + diff --git a/src/common/transformations/src/transformations/mlir/op/matmul.cpp b/src/common/transformations/src/transformations/mlir/op/matmul.cpp index f125e40ed409b6..229b4734358cfd 100644 --- a/src/common/transformations/src/transformations/mlir/op/matmul.cpp +++ b/src/common/transformations/src/transformations/mlir/op/matmul.cpp @@ -20,6 +20,7 @@ struct ConvertMatMul { void operator()(ConversionContext& context, NodePtr node) { auto matmul_node = std::dynamic_pointer_cast(node); assert(matmul_node); + // FIXME: current code limitation assert(!matmul_node->get_transpose_a() && matmul_node->get_transpose_b()); @@ -29,20 +30,9 @@ struct ConvertMatMul { const auto inputs = context.getInputs(node); const auto ov_output_element_type = node->get_output_element_type(0); const auto ov_output_shape = node->get_output_partial_shape(0); - auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); // Instead of this (WRONG): cast(inputs[0].getType()); - - llvm::SmallVector dynamicSizes; - for (auto [idx, dim] : llvm::enumerate(outType.getShape())) { - if (!mlir::ShapedType::isDynamic(dim)) - continue; - // FIXME: correct in case if (!transpose_a && transpose_b) - auto dimSize = - builder.create(loc, - idx == 0 ? inputs[0] : inputs[1], - 0); // TODO: Use symbols instead of taking dims directly from inputs - dynamicSizes.push_back(dimSize); - } - auto empty = builder.create(loc, outType, dynamicSizes); + auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); + auto dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); + auto empty = builder.create(loc, outType, dynamic_dimensions); auto zero = getConstant(builder, ov_output_element_type, 0); auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); // TODO: Add other variants of transpose_a/transpose_b diff --git a/src/common/transformations/src/transformations/mlir/op/relu.cpp b/src/common/transformations/src/transformations/mlir/op/relu.cpp index a25f571f61cddf..116fd24de7229a 100644 --- a/src/common/transformations/src/transformations/mlir/op/relu.cpp +++ b/src/common/transformations/src/transformations/mlir/op/relu.cpp @@ -19,22 +19,12 @@ struct ConvertRelu { void operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); - // TODO: Support broadcasts const auto input = context.getInputs(node)[0]; const auto ov_output_element_type = node->get_output_element_type(0); const auto ov_output_shape = node->get_output_partial_shape(0); auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); - // Named unary ops directly overwrite data in `outs` buffer so, there is no need to provide non-empty - // destination at the tensor-level. - // Use `tensor.empty` to avoid temporary buffer allocation and memcpy after bufferization. - llvm::SmallVector dynamicSizes; - for (auto [idx, dim] : llvm::enumerate(outType.getShape())) { - if (!mlir::ShapedType::isDynamic(dim)) - continue; - auto dimSize = builder.create(loc, input, idx); - dynamicSizes.push_back(dimSize); - } - auto empty = builder.create(loc, outType, dynamicSizes); + auto dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); + auto empty = builder.create(loc, outType, dynamic_dimensions); auto zero = getConstant(builder, ov_output_element_type, 0); auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); auto relu = From 3e1871be23e4bbff3a2e2edae830fba3cffc5aee Mon Sep 17 00:00:00 2001 From: Sergey Lyalin Date: Fri, 26 Jul 2024 13:14:42 +0400 Subject: [PATCH 018/121] Disable MatMul conversion for not supported cases to avoid crashing (#151) * Check MatMul expected attributes in a predicate instead of assert in the transformation callback. Fixes PyTorch addmm layer tests. * Put rank == 2 restriction on MatMul conversion. Fixes PyTorch linear layer tests. --- .../src/transformations/mlir/op/matmul.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/op/matmul.cpp b/src/common/transformations/src/transformations/mlir/op/matmul.cpp index 229b4734358cfd..635779c0bf52fe 100644 --- a/src/common/transformations/src/transformations/mlir/op/matmul.cpp +++ b/src/common/transformations/src/transformations/mlir/op/matmul.cpp @@ -18,12 +18,6 @@ using namespace ov::mlir; struct ConvertMatMul { void operator()(ConversionContext& context, NodePtr node) { - auto matmul_node = std::dynamic_pointer_cast(node); - assert(matmul_node); - - // FIXME: current code limitation - assert(!matmul_node->get_transpose_a() && matmul_node->get_transpose_b()); - auto loc = createLocation(context.context, node); auto& builder = context.builder(); // TODO: Support broadcasts @@ -50,7 +44,16 @@ using namespace ov::pass::pattern; using namespace ov::op; MatMulPattern::MatMulPattern() : MarkPattern( - wrap_type({any_input(), any_input()}), + wrap_type({any_input(), any_input()}, [](const Output& output) { + auto node = std::dynamic_pointer_cast(output.get_node_shared_ptr()); + assert(node); + // FIXME: current code limitation + return + !has_dynamic_rank(node) && + !node->get_transpose_a() && node->get_transpose_b() && + node->get_input_partial_shape(0).rank().get_length() == 2 && + node->get_input_partial_shape(1).rank().get_length() == 2; + }), ConvertMatMul()) { } From f8bbb6a28bf8f966cdbf96b2de3c9cbf32237940 Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Fri, 26 Jul 2024 15:00:50 +0200 Subject: [PATCH 019/121] More matmul variants (#153) Adds support for `linalg.matmul` and `linalg.matmul_tranpose_a`. --- .../src/transformations/mlir/op/matmul.cpp | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/op/matmul.cpp b/src/common/transformations/src/transformations/mlir/op/matmul.cpp index 635779c0bf52fe..b8c50db1ed4f31 100644 --- a/src/common/transformations/src/transformations/mlir/op/matmul.cpp +++ b/src/common/transformations/src/transformations/mlir/op/matmul.cpp @@ -29,8 +29,25 @@ struct ConvertMatMul { auto empty = builder.create(loc, outType, dynamic_dimensions); auto zero = getConstant(builder, ov_output_element_type, 0); auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); - // TODO: Add other variants of transpose_a/transpose_b - auto matmul = builder.create(loc, mlir::ValueRange{inputs[0], inputs[1]}, mlir::ValueRange{fill.getResult(0)}); + + mlir::SmallVector ins{inputs[0], inputs[1]}; + mlir::SmallVector outs{fill.getResult(0)}; + + auto matmul_node = std::dynamic_pointer_cast(node); + assert(matmul_node); + bool isTransposedA = matmul_node->get_transpose_a(); + bool isTransposedB = matmul_node->get_transpose_b(); + assert(!(isTransposedA && isTransposedB)); + + Operation* matmul; + if (isTransposedA) { + matmul = builder.create(loc, ins, outs); + } else if (isTransposedB) { + matmul = builder.create(loc, ins, outs); + } else { + matmul = builder.create(loc, ins, outs); + } + context.addOutputs(node, matmul); } }; @@ -48,11 +65,9 @@ MatMulPattern::MatMulPattern() : MarkPattern( auto node = std::dynamic_pointer_cast(output.get_node_shared_ptr()); assert(node); // FIXME: current code limitation - return - !has_dynamic_rank(node) && - !node->get_transpose_a() && node->get_transpose_b() && - node->get_input_partial_shape(0).rank().get_length() == 2 && - node->get_input_partial_shape(1).rank().get_length() == 2; + return !has_dynamic_rank(node) && !(node->get_transpose_a() && node->get_transpose_b()) && + node->get_input_partial_shape(0).rank().get_length() == 2 && + node->get_input_partial_shape(1).rank().get_length() == 2; }), ConvertMatMul()) { } From e6241fd1877567b3fdebd8a02fe88bf0660318f7 Mon Sep 17 00:00:00 2001 From: Sergey Lyalin Date: Fri, 26 Jul 2024 19:27:31 +0400 Subject: [PATCH 020/121] Environment variables to control MLIR execution (#154) * Check MatMul expected attributes in a predicate instead of assert in the transformation callback. Fixes PyTorch addmm layer tests. * Put rank == 2 restriction on MatMul conversion. Fixes PyTorch linear layer tests. * Enable MLIR and TPP-MLIR activation via environment variables * Added debug macros controlled by OV_MLIR_DEBUG env variable. Disabled debug prints by default. --- .../src/transformations/mlir/convert.cpp | 80 ++++++--- .../transformations/mlir/convert_common.cpp | 6 + .../transformations/mlir/convert_common.hpp | 7 + .../src/transformations/mlir/mlir_op.cpp | 169 +++++++++--------- .../src/transformations/mlir/mlir_op.hpp | 2 +- 5 files changed, 155 insertions(+), 109 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index 63bafdc0d5fa1a..b5a076749ee5fc 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include // TODO: Prune unused headers -- it's hard to understand needed ones @@ -187,7 +188,7 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, // This pass converts a group of nodes into a single MLIROp -NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph) { +NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph, bool tpp_mlir_enabled) { mlir::OwningOpRef module = ngraph_to_mlir(context, subgraph->inputs, subgraph->nodes, subgraph->outputs); const auto& inputs = subgraph->inputs; @@ -203,8 +204,9 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph) { if(0 == input_map.count(symbol)) { input_map[symbol] = Index(i, j); } else { - std::cerr << "[ DEBUG ] Lost equality constraint for dimensions in output " << input << "\n" - << " If the constraint is violated in runtime it will result in the undefined behaviour.\n"; + OPENVINO_MLIR_DEBUG_PRINT( + "[ DEBUG ] Lost equality constraint for dimensions in output " << input << ".\n" << + " If the constraint is violated in runtime it will result in the undefined behaviour.\n"); } } } @@ -230,7 +232,7 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph) { } return std::make_shared( subgraph->inputs, - std::make_shared(std::move(module)), + std::make_shared(std::move(module), tpp_mlir_enabled), output_types, output_map ); @@ -251,16 +253,20 @@ void replace_subgraph(SubgraphPtr subgraph, NodePtr node) { class Partitioner : public ov::pass::ModelPass { MLIRContext* context; + bool tpp_mlir_enabled; public: OPENVINO_RTTI("Partitioner"); - Partitioner(MLIRContext* context) : context(context) {} + Partitioner(MLIRContext* context, bool tpp_mlir_enabled) : + context(context), + tpp_mlir_enabled(tpp_mlir_enabled) + {} bool run_on_model(const std::shared_ptr& model) override { SubgraphTracker tracker([this](SubgraphPtr subgraph) { - auto mlir_op = ngraph_to_mlir_op(context, subgraph); + auto mlir_op = ngraph_to_mlir_op(context, subgraph, tpp_mlir_enabled); replace_subgraph(subgraph, mlir_op); - std::cerr << "Created MLIR op: " << mlir_op << "\n"; + OPENVINO_MLIR_DEBUG_PRINT("Created MLIR op: " << mlir_op << "\n"); } ); for(auto node: model->get_ordered_ops()) { @@ -272,7 +278,7 @@ class Partitioner : public ov::pass::ModelPass { }; -void injectMLIR(std::shared_ptr model, MLIRContext* context) { +void injectMLIR(std::shared_ptr model, MLIRContext* context, bool tpp_mlir_enabled) { ov::pass::Manager manager; using namespace ov::op; manager.set_per_pass_validation(false); @@ -283,17 +289,26 @@ void injectMLIR(std::shared_ptr model, MLIRContext* context) { manager.register_pass>(ov::element::f32); manager.register_pass(); manager.register_pass(); - manager.register_pass(context); + manager.register_pass(context, tpp_mlir_enabled); manager.run_passes(model); model->validate_nodes_and_infer_types(); } -MLIRContext* get_shared_mlir_context() { +MLIRContext* get_shared_mlir_context(bool tpp_mlir_enabled_current) { // Gives MLIRContext instance shared for entire OV process and initialized once upon the initial request // FIXME: Bind with OpenVINO lifetime in the sutable class instead of dirty tricking with static lifetime static std::shared_ptr context; + static bool tpp_mlir_enabled = tpp_mlir_enabled_current; + + if(context) { + if(tpp_mlir_enabled_current != tpp_mlir_enabled) { + OPENVINO_MLIR_DEBUG_PRINT("[ DEBUG ] Switched TPP mode, reinitialize MLIR context\n"); + tpp_mlir_enabled = tpp_mlir_enabled_current; + context.reset(); + } + } if (!context) { @@ -301,24 +316,28 @@ MLIRContext* get_shared_mlir_context() { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); - std::cerr << "[ DEBUG ] Using TPP_MLIR: "; - #if TPP_MLIR + OPENVINO_MLIR_DEBUG_PRINT("[ DEBUG ] Using TPP_MLIR: "); + if(tpp_mlir_enabled) { + OPENVINO_MLIR_DEBUG_PRINT("YES\n"); // Initialize GPU-related LLVM machinery - tpp::initializeGpuTargets(); - std::cerr << "YES\n"; - #else - std::cerr << "NO\n"; - #endif + #ifdef TPP_MLIR + tpp::initializeGpuTargets(); + #endif + } else { + OPENVINO_MLIR_DEBUG_PRINT("NO\n"); + } // Add the following to include *all* MLIR Core dialects, or selectively // include what you need like above. You only need to register dialects that // will be *parsed* by the tool, not the one generated DialectRegistry registry; - #if TPP_MLIR - registry.insert(); - registry.insert(); - registry.insert(); - #endif + if(tpp_mlir_enabled) { + #ifdef TPP_MLIR + registry.insert(); + registry.insert(); + registry.insert(); + #endif + } registerAllDialects(registry); registerAllExtensions(registry); @@ -339,5 +358,20 @@ MLIRContext* get_shared_mlir_context() { } // namespace void ov::pass::transformMLIR(std::shared_ptr model) { - injectMLIR(model, get_shared_mlir_context()); + if(util::getenv_bool("OV_MLIR", true)) { + bool tpp_mlir_default = + #ifdef TPP_MLIR + true; + #else + false; + #endif + bool tpp_mlir_enabled = util::getenv_bool("OV_MLIR_TPP", tpp_mlir_default); + #ifndef TPP_MLIR + OPENVINO_ASSERT(!tpp_mlir_enabled, + "[ ERROR ] OpenVINO wasn't compiled with TPP_MLIR support, " + "but OV_MLIR_TPP environment variable is set to enable it."); + #endif + + injectMLIR(model, get_shared_mlir_context(tpp_mlir_enabled), tpp_mlir_enabled); + } } diff --git a/src/common/transformations/src/transformations/mlir/convert_common.cpp b/src/common/transformations/src/transformations/mlir/convert_common.cpp index 1499acb0ba844f..de8782c77c12bc 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.cpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.cpp @@ -4,6 +4,9 @@ #include "convert_common.hpp" +#include + + namespace { using namespace mlir; @@ -58,6 +61,9 @@ IntegerType getBool8Type(MLIRContext* ctx) { namespace ov { namespace mlir { +bool is_debug() { + util::getenv_bool("OV_MLIR_DEBUG", false); +} Location createLayerLocation(MLIRContext* ctx, const std::string& layerName, const std::string& layerType) { const auto layerNameAttr = StringAttr::get(ctx, layerName); diff --git a/src/common/transformations/src/transformations/mlir/convert_common.hpp b/src/common/transformations/src/transformations/mlir/convert_common.hpp index 6622ea5ed70c0c..d6f794bb24bb12 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.hpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.hpp @@ -4,6 +4,8 @@ #pragma once +#include + #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/MLIRContext.h" @@ -17,6 +19,11 @@ namespace ov { namespace mlir { +bool is_debug(); + +#define OPENVINO_MLIR_DEBUG(X) do if(::ov::mlir::is_debug()) { X; } while(false) +#define OPENVINO_MLIR_DEBUG_PRINT(X) do if(::ov::mlir::is_debug()) { ::std::cerr << X; } while(false) + using namespace ::mlir; Location createLayerLocation(MLIRContext* ctx, const std::string& layerName, const std::string& layerType); diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index eea1019f60db91..74dee5e4db8393 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -65,77 +65,73 @@ using namespace mlir; using NodePtr = std::shared_ptr; using SymbolPtr = std::shared_ptr; -void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module) { - // A set of default passes that lower any input IR to LLVM +void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module, bool tpp_mlir_enabled) { PassManager pm(module->getContext()); - -#if TPP_MLIR - - tpp::DefaultPipelineOptions defPipelineOpts; - pm.addPass(tpp::createDefaultPipeline(defPipelineOpts)); - -#else // Simplified default lowering to LLVM from LLVM tests - - // Cleanup before bufferization. - // Simplifies IR to allow better bufferization. - pm.addNestedPass(createCanonicalizerPass()); - pm.addNestedPass(createCSEPass()); - - // Remove empty tensors to avoid converting them into temporary buffers. - pm.addPass(bufferization::createEmptyTensorEliminationPass()); - - pm.addPass(bufferization::createOneShotBufferizePass()); - pm.addNestedPass(bufferization::createFinalizingBufferizePass()); - - // Cleanup after bufferization - possibly remove redundant copies. - pm.addNestedPass(createCanonicalizerPass()); - pm.addNestedPass(createCSEPass()); - - // Deallocation pipeline to avoid memory leaks from created temporary buffers. - pm.addPass(memref::createExpandReallocPass(/*emitDeallocs=*/false)); - pm.addPass(createCanonicalizerPass()); - bufferization::DeallocationOptions deallocOpts; - deallocOpts.privateFuncDynamicOwnership = false; - pm.addPass(bufferization::createOwnershipBasedBufferDeallocationPass(deallocOpts)); - pm.addPass(createCanonicalizerPass()); - pm.addPass(bufferization::createBufferDeallocationSimplificationPass()); - pm.addPass(bufferization::createLowerDeallocationsPass()); - pm.addPass(createCSEPass()); - pm.addPass(createCanonicalizerPass()); - - // Blanket-convert any remaining high-level vector ops to loops if any remain. - pm.addNestedPass(createConvertVectorToSCFPass()); - // pm.addNestedPass(createLinalgGeneralizeNamedOpsPass()); - // Blanket-convert any remaining linalg ops to loops if any remain. - pm.addNestedPass(createConvertLinalgToLoopsPass()); - // Blanket-convert any remaining affine ops if any remain. - pm.addPass(createLowerAffinePass()); - // Convert SCF to CF (always needed). - pm.addPass(createConvertSCFToCFPass()); - // Sprinkle some cleanups. - pm.addPass(createCanonicalizerPass()); - pm.addPass(createCSEPass()); - // Blanket-convert any remaining linalg ops to LLVM if any remain. - // pm.addPass(createConvertLinalgToLLVMPass()); // no such pass - // Convert vector to LLVM (always needed). - pm.addPass(createConvertVectorToLLVMPass()); - // Convert Math to LLVM (always needed). - pm.addNestedPass(createConvertMathToLLVMPass()); - // Expand complicated MemRef operations before lowering them. - pm.addPass(memref::createExpandStridedMetadataPass()); - // The expansion may create affine expressions. Get rid of them. - pm.addPass(createLowerAffinePass()); - // Convert MemRef to LLVM (always needed). - // pm.addPass(memref::createExpandOpsPass()); - pm.addPass(createFinalizeMemRefToLLVMConversionPass()); - // Convert Func to LLVM (always needed). - pm.addPass(createConvertFuncToLLVMPass()); - // Convert Index to LLVM (always needed). - pm.addPass(createConvertIndexToLLVMPass()); - // Convert remaining unrealized_casts (always needed). - pm.addPass(createReconcileUnrealizedCastsPass()); - -#endif + if(tpp_mlir_enabled) { + #ifdef TPP_MLIR + tpp::DefaultPipelineOptions defPipelineOpts; + pm.addPass(tpp::createDefaultPipeline(defPipelineOpts)); + #endif + } else { + // Cleanup before bufferization. + // Simplifies IR to allow better bufferization. + pm.addNestedPass(createCanonicalizerPass()); + pm.addNestedPass(createCSEPass()); + + // Remove empty tensors to avoid converting them into temporary buffers. + pm.addPass(bufferization::createEmptyTensorEliminationPass()); + + pm.addPass(bufferization::createOneShotBufferizePass()); + pm.addNestedPass(bufferization::createFinalizingBufferizePass()); + + // Cleanup after bufferization - possibly remove redundant copies. + pm.addNestedPass(createCanonicalizerPass()); + pm.addNestedPass(createCSEPass()); + + // Deallocation pipeline to avoid memory leaks from created temporary buffers. + pm.addPass(memref::createExpandReallocPass(/*emitDeallocs=*/false)); + pm.addPass(createCanonicalizerPass()); + bufferization::DeallocationOptions deallocOpts; + deallocOpts.privateFuncDynamicOwnership = false; + pm.addPass(bufferization::createOwnershipBasedBufferDeallocationPass(deallocOpts)); + pm.addPass(createCanonicalizerPass()); + pm.addPass(bufferization::createBufferDeallocationSimplificationPass()); + pm.addPass(bufferization::createLowerDeallocationsPass()); + pm.addPass(createCSEPass()); + pm.addPass(createCanonicalizerPass()); + + // Blanket-convert any remaining high-level vector ops to loops if any remain. + pm.addNestedPass(createConvertVectorToSCFPass()); + // pm.addNestedPass(createLinalgGeneralizeNamedOpsPass()); + // Blanket-convert any remaining linalg ops to loops if any remain. + pm.addNestedPass(createConvertLinalgToLoopsPass()); + // Blanket-convert any remaining affine ops if any remain. + pm.addPass(createLowerAffinePass()); + // Convert SCF to CF (always needed). + pm.addPass(createConvertSCFToCFPass()); + // Sprinkle some cleanups. + pm.addPass(createCanonicalizerPass()); + pm.addPass(createCSEPass()); + // Blanket-convert any remaining linalg ops to LLVM if any remain. + // pm.addPass(createConvertLinalgToLLVMPass()); // no such pass + // Convert vector to LLVM (always needed). + pm.addPass(createConvertVectorToLLVMPass()); + // Convert Math to LLVM (always needed). + pm.addNestedPass(createConvertMathToLLVMPass()); + // Expand complicated MemRef operations before lowering them. + pm.addPass(memref::createExpandStridedMetadataPass()); + // The expansion may create affine expressions. Get rid of them. + pm.addPass(createLowerAffinePass()); + // Convert MemRef to LLVM (always needed). + // pm.addPass(memref::createExpandOpsPass()); + pm.addPass(createFinalizeMemRefToLLVMConversionPass()); + // Convert Func to LLVM (always needed). + pm.addPass(createConvertFuncToLLVMPass()); + // Convert Index to LLVM (always needed). + pm.addPass(createConvertIndexToLLVMPass()); + // Convert remaining unrealized_casts (always needed). + pm.addPass(createReconcileUnrealizedCastsPass()); + } auto result = pm.run(module.get()); if (failed(result)) { @@ -225,7 +221,7 @@ struct MemRef { assert(byte_strides[i] % element_size == 0); // TODO: handle case when stride is not aligned (restrict at OV API level) strides[i] = byte_strides[i] / element_size; - //std::cout << "stride [" << i << "] = " << strides[i] << "\n"; + //std::cerr << "stride [" << i << "] = " << strides[i] << "\n"; } } @@ -256,22 +252,24 @@ namespace mlir { using namespace ::mlir; -MLIREvaluate::MLIREvaluate(OwningOpRef _module) : module(std::move(_module)) { - if (true) { - std::cerr << "[ DEBUG ] Source MLIR:\n"; - std::cerr << "-----------------------------------------\n"; - module->dump(); - std::cerr << "-----------------------------------------\n"; - } +MLIREvaluate::MLIREvaluate(OwningOpRef _module, bool tpp_mlir_enabled) : + module(std::move(_module)) { - prepareMLIRKernelWithoutWrapper(module); + OPENVINO_MLIR_DEBUG_PRINT( + "[ DEBUG ] Source MLIR:\n" + "-----------------------------------------\n"); + OPENVINO_MLIR_DEBUG(module->dump()); + OPENVINO_MLIR_DEBUG_PRINT( + "-----------------------------------------\n"); - if (true) { - std::cerr << "[ DEBUG ] Target LLVM:\n"; - std::cerr << "-----------------------------------------\n"; - module->dump(); - std::cerr << "-----------------------------------------\n"; - } + prepareMLIRKernelWithoutWrapper(module, tpp_mlir_enabled); + + OPENVINO_MLIR_DEBUG_PRINT( + "[ DEBUG ] Target LLVM:\n" + "-----------------------------------------\n"); + OPENVINO_MLIR_DEBUG(module->dump()); + OPENVINO_MLIR_DEBUG_PRINT( + "-----------------------------------------\n"); auto optPipeline = mlir::makeOptimizingTransformer(2, /*sizeLevel=*/0, // FIXME: HARDCODED @@ -324,6 +322,7 @@ bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) memref_args.push_back(MemRef(inputs[i])); } for (size_t i = 0; i < outputs.size(); ++i) { + // TODO: Optimize by adding all dimensions to dimensions_map, not only dynamic Shape target; PartialShape expected = get_output_partial_shape(i); for(size_t j = 0; j < expected.size(); ++j) { diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.hpp b/src/common/transformations/src/transformations/mlir/mlir_op.hpp index 59248f1d14641d..72fbcc462cd805 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.hpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.hpp @@ -29,7 +29,7 @@ class MLIREvaluate { public: - MLIREvaluate(OwningOpRef _module); + MLIREvaluate(OwningOpRef _module, bool tpp_mlir_enabled); bool invoke_packed(std::vector& args); }; From ea8e4a3bfe7232999289ca1b0be068907f178070 Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Mon, 29 Jul 2024 18:33:20 +0200 Subject: [PATCH 021/121] Match all element types (#156) Relaxes MLIR conversion matchers to accept any element type. --- .../transformations/src/transformations/mlir/convert.cpp | 8 ++++---- .../src/transformations/mlir/convert_common.cpp | 5 +---- .../src/transformations/mlir/convert_common.hpp | 7 +------ .../transformations/src/transformations/mlir/op/relu.cpp | 3 +-- 4 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index b5a076749ee5fc..da777e30094da1 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -283,10 +283,10 @@ void injectMLIR(std::shared_ptr model, MLIRContext* context, bool tpp using namespace ov::op; manager.set_per_pass_validation(false); manager.register_pass(); - manager.register_pass>(ov::element::f32); - manager.register_pass>(ov::element::f32); - manager.register_pass>(ov::element::f32); - manager.register_pass>(ov::element::f32); + manager.register_pass>(); + manager.register_pass>(); + manager.register_pass>(); + manager.register_pass>(); manager.register_pass(); manager.register_pass(); manager.register_pass(context, tpp_mlir_enabled); diff --git a/src/common/transformations/src/transformations/mlir/convert_common.cpp b/src/common/transformations/src/transformations/mlir/convert_common.cpp index de8782c77c12bc..45ab7a904f0d90 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.cpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.cpp @@ -134,10 +134,7 @@ Location createLocation(MLIRContext* ctx, NodePtr node) { return createLayerLocation(ctx, node->get_friendly_name(), node->get_type_name()); } -bool elementwise_no_broadcast_predicate_impl(const ov::Output& output, ov::element::Type type) { - if (output.get_element_type() != type) { - return false; - } +bool elementwise_no_broadcast_predicate(const ov::Output& output) { if (has_dynamic_rank(output.get_node_shared_ptr())) { return false; } diff --git a/src/common/transformations/src/transformations/mlir/convert_common.hpp b/src/common/transformations/src/transformations/mlir/convert_common.hpp index d6f794bb24bb12..5b6b83ba6d3d5f 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.hpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.hpp @@ -38,12 +38,7 @@ RankedTensorType importTensor(MLIRContext* ctx, Location createLocation(MLIRContext* ctx, NodePtr node); -bool elementwise_no_broadcast_predicate_impl(const ov::Output& output, ov::element::Type type); - -template -bool elementwise_no_broadcast_predicate(const ov::Output& output) { - return elementwise_no_broadcast_predicate_impl(output, type); -} +bool elementwise_no_broadcast_predicate(const ov::Output& output); // Borrowed it from TPP-MLIR. FIXME: Do we have a better upstreamed alternative? template diff --git a/src/common/transformations/src/transformations/mlir/op/relu.cpp b/src/common/transformations/src/transformations/mlir/op/relu.cpp index 116fd24de7229a..6f7157f9bd4bd4 100644 --- a/src/common/transformations/src/transformations/mlir/op/relu.cpp +++ b/src/common/transformations/src/transformations/mlir/op/relu.cpp @@ -42,8 +42,7 @@ using namespace ov::pass::pattern; using namespace ov::op; ReluPattern::ReluPattern() - : MarkPattern(wrap_type({any_input()}, elementwise_no_broadcast_predicate), - ConvertRelu()) {} + : MarkPattern(wrap_type({any_input()}, elementwise_no_broadcast_predicate), ConvertRelu()) {} } // namespace mlir } // namespace ov From acbd92577dd963e1c99a834a3e7d2362ebbbaf7a Mon Sep 17 00:00:00 2001 From: Sergey Lyalin Date: Tue, 30 Jul 2024 17:35:24 +0400 Subject: [PATCH 022/121] Fix graph partitioner when two MLIROp instances go side-by-side. Fix in return statement in is_dubug function. (#157) --- .../src/transformations/mlir/convert_common.cpp | 2 +- .../src/transformations/mlir/subgraph_tracker.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/convert_common.cpp b/src/common/transformations/src/transformations/mlir/convert_common.cpp index 45ab7a904f0d90..dee142a554fe79 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.cpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.cpp @@ -62,7 +62,7 @@ namespace ov { namespace mlir { bool is_debug() { - util::getenv_bool("OV_MLIR_DEBUG", false); + return util::getenv_bool("OV_MLIR_DEBUG", false); } Location createLayerLocation(MLIRContext* ctx, const std::string& layerName, const std::string& layerType) { diff --git a/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp b/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp index 1459ae34ef4c07..6eb56846145510 100644 --- a/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp +++ b/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp @@ -116,7 +116,11 @@ void SubgraphTracker::set_dependencies(NodePtr node, const Dependencies& depende // set/get subgraph id that a give node belongs to SubgraphID SubgraphTracker::get_subgraph_id(NodePtr node) { - auto id = node->get_rt_info().at("__subgraph_id").as(); + const auto& rti = node->get_rt_info(); + if(!rti.count("__subgraph_id")) { + return nullptr; + } + auto id = rti.at("__subgraph_id").as(); if(id) { id = ov::symbol::ancestor_of(id); } From df9b3e00405fc500cdb313467e628767cf91b552 Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Tue, 30 Jul 2024 16:06:49 +0200 Subject: [PATCH 023/121] MLP benchmarks (#152) Collection of MLP benchmarks using combination of OV and TPP-MLIR. --- cmake/tpp-mlir.cmake | 2 + tools/mlir_bench/README.md | 72 ++++++++ tools/mlir_bench/libxsmm_bench.sh | 70 ++++++++ tools/mlir_bench/mlp_bench.sh | 109 ++++++++++++ tools/mlir_bench/ov_model_gen.py | 247 ++++++++++++++++++++++++++ tools/mlir_bench/ov_raw_mlir_bench.sh | 101 +++++++++++ tools/mlir_bench/tpp_mlir_bench.sh | 79 ++++++++ 7 files changed, 680 insertions(+) create mode 100644 tools/mlir_bench/README.md create mode 100755 tools/mlir_bench/libxsmm_bench.sh create mode 100755 tools/mlir_bench/mlp_bench.sh create mode 100644 tools/mlir_bench/ov_model_gen.py create mode 100755 tools/mlir_bench/ov_raw_mlir_bench.sh create mode 100755 tools/mlir_bench/tpp_mlir_bench.sh diff --git a/cmake/tpp-mlir.cmake b/cmake/tpp-mlir.cmake index 300ac99df3b734..d988ac7a919271 100644 --- a/cmake/tpp-mlir.cmake +++ b/cmake/tpp-mlir.cmake @@ -36,6 +36,8 @@ if (TPP_MLIR_DIR) -Wl,--no-as-needed -L${TPP_MLIR_DIR}/lib -ltpp_xsmm_runner_utils + -L${LLVM_LIBRARY_DIR} + -lmlir_c_runner_utils -Wl,--as-needed ) #FIXME: Provide platform-independent way of doing that: diff --git a/tools/mlir_bench/README.md b/tools/mlir_bench/README.md new file mode 100644 index 00000000000000..85dcdb65dccdb3 --- /dev/null +++ b/tools/mlir_bench/README.md @@ -0,0 +1,72 @@ +# MLP benchmarks + +Various MLP benchmarks. +Describes usage of the `*_bench.sh` scripts. + +## LIBXSMM +- F32: +```bash +libxsmm_bench.sh +``` +- BF16: +```bash +libxsmm_bench.sh -B +``` + +## Pure MLIR +- F32: +```bash +tpp_mlir_bench.sh -t f32 +``` +- BF16: +```bash +tpp_mlir_bench.sh -t bf16 +``` + +## OV - no MLIR +Default model:\ +`matmul_transpose_b + bias broadcast` + +Alternative model - scritp flag `-b mlp`:\ +`matmul + bias (no broadcast)` + +- F32: +```bash +OV_MLIR=0 mlp_bench.sh -t f32 +``` +- BF16: +```bash +OV_MLIR=0 mlp_bench.sh -t bf16 +``` + +## OV + MLIR - full +Default model:\ +`matmul_transpose_b + bias broadcast` + +Alternative model - scritp flag `-b mlp`:\ +`matmul + bias (no broadcast)` + +- F32: +```bash +OV_MLIR=1 mlp_bench.sh -t f32 +``` +- BF16: +```bash +OV_MLIR=1 mlp_bench.sh -t bf16 +``` + +## OV + MLIR - kernel only +Default model:\ +`matmul_transpose_b + bias broadcast` + +Alternative model - scritp flag `-b mlp`:\ +`matmul + bias (no broadcast)` + +- F32: +```bash +ov_raw_mlir_bench.sh -t f32 +``` +- BF16: +```bash +ov_raw_mlir_bench.sh -t bf16 +``` diff --git a/tools/mlir_bench/libxsmm_bench.sh b/tools/mlir_bench/libxsmm_bench.sh new file mode 100755 index 00000000000000..b39da1d187ceae --- /dev/null +++ b/tools/mlir_bench/libxsmm_bench.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +# Runs MLP benchmarks using libxsmm. + +die_syntax() { + echo "Syntax: $0 [-B] [-D]" + echo "" + echo " -B: Use bf16 data type" + echo " -D: Set model shapes to dynamic" + exit 1 +} + +# Cmd-line opts +while getopts "BD" arg; do + case ${arg} in + B) + DATA_TYPE="bf16" + ;; + D) + IS_DYNAMIC=true + ;; + ?) + echo "Invalid option: ${OPTARG}" + die_syntax + ;; + esac +done + +BENCH_RUNNER=xsmm_dnn_mlp + +# Initial validation. +if ! [ "$(command -v ${BENCH_RUNNER})" ]; then + echo "Missing benchmark runner ${BENCH_RUNNER}" + exit 1 +fi +if [ ${IS_DYNAMIC} ]; then + echo "Dynamic shapes are not supported by ${BENCH_RUNNER}" + exit 1 +fi + +# Kernel config. +INPUT_SIZES=( 1024 2048 4096 8192 ) +OUTPUT_SIZES=( 128 256 512 ) +if [ ! "${DATA_TYPE}" ]; then + DATA_TYPE="f32" +fi + +echo "Result type: GFLOPS" +for OUT_SIZE in "${OUTPUT_SIZES[@]}"; do + echo "MLP - OUT: ${OUT_SIZE} INS: ${INPUT_SIZES[@]}" + for IN_SIZE in "${INPUT_SIZES[@]}"; do + # Run benchmark. + NUM_ITER=10000 + FUSE_TYPE=5 + TYPE=F + TILES=(64 64 64) + LAYOUT=(0 0) + if [ "${DATA_TYPE}" = "bf16" ]; then + LAYOUT=(1 1) + fi + # Disable parallelism. + ENV_FLAGS=OMP_NUM_THREADS=1 + exec env ${ENV_FLAGS} ${BENCH_RUNNER} ${NUM_ITER} ${OUT_SIZE} ${FUSE_TYPE} ${TYPE} ${TILES[@]} \ + ${LAYOUT[@]} ${IN_SIZE} ${OUT_SIZE} \ + | sed -nE "s/.*GFLOPS\s+=\s*([0-9.]+).*/\\1/p" + done +done diff --git a/tools/mlir_bench/mlp_bench.sh b/tools/mlir_bench/mlp_bench.sh new file mode 100755 index 00000000000000..ca52ee995d01cd --- /dev/null +++ b/tools/mlir_bench/mlp_bench.sh @@ -0,0 +1,109 @@ +#!/bin/bash + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +# Runs OV MLP benchmarks. + +die_syntax() { + echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-b (mlp)] [-D]" + echo "" + echo " -t: Optional data type" + echo " -b: Optional baseline model" + echo " -D: Set model shapes to dynamic" + exit 1 +} + +# Cmd-line opts +while getopts "t:b:D" arg; do + case ${arg} in + t) + DATA_TYPE=${OPTARG} + ;; + b) + BASELINE_MODEL=${OPTARG} + ;; + D) + IS_DYNAMIC=true + ;; + ?) + echo "Invalid option: ${OPTARG}" + die_syntax + ;; + esac +done + +OV_ROOT=$(git rev-parse --show-toplevel) +BENCH_ROOT=$(realpath ${OV_ROOT}/tools/mlir_bench) + +MODEL_GEN=$(realpath ${BENCH_ROOT}/ov_model_gen.py) +BENCH_RUNNER=benchmark_app + +# Initial validation. +if ! [ -d ${OV_ROOT} ]; then + echo "Missing OV repo" + exit 1 +fi +if ! [ -d ${BENCH_ROOT} ]; then + echo "Missing MLIR benchmark directory" + exit 1 +fi +if ! [ -f ${MODEL_GEN} ]; then + echo "Missing model generator" + exit 1 +fi +if ! [ "$(command -v ${BENCH_RUNNER})" ]; then + echo "Missing benchmark runner ${BENCH_RUNNER}" + exit 1 +fi +if [ "${BASELINE_MODEL}" ] && [ ${IS_DYNAMIC} ]; then + echo "Baseline models with dynamic shapes not supported" + exit 1 +fi + +# Kernel config. +INPUT_SIZES=( 1024 2048 4096 8192 ) +OUTPUT_SIZES=( 128 256 512 ) +if [ ! "${DATA_TYPE}" ]; then + DATA_TYPE="f32" +fi +MODEL_NAME="MLIR_MLP_BENCH.xml" + +echo "Result type: time [ms]" +for OUT_SIZE in "${OUTPUT_SIZES[@]}"; do + echo "MLP - OUT: ${OUT_SIZE} INS: ${INPUT_SIZES[@]}" + for IN_SIZE in "${INPUT_SIZES[@]}"; do + # Generate model. + if [ "${BASELINE_MODEL}" ]; then + # Enable baseline model flag. + MODEL_CONFIG=(-b="${BASELINE_MODEL}[${OUT_SIZE},${OUT_SIZE},${IN_SIZE}]") + else + # Generate default PyTorch MLP. + MODEL_CONFIG=(-l="linear[${IN_SIZE},${OUT_SIZE}] relu[]") + fi + GEN_FLAGS=(-t ${DATA_TYPE} -n ${MODEL_NAME}) + if [ ${IS_DYNAMIC} ]; then + GEN_FLAGS+=(--dynamic) + fi + python3 ${MODEL_GEN} "${MODEL_CONFIG[@]}" "${GEN_FLAGS[@]}" + if [ $? != 0 ]; then + echo "Failed to generate model" + exit 1 + fi + # Run benchmark. + PRECISION=${DATA_TYPE} + if [ "${DATA_TYPE}" = "bf16" ]; then + # No native support for bf16, use simple f16 instead. + PRECISION="f16" + fi + if [ ${IS_DYNAMIC} ]; then + DATA_SHAPE=(-data_shape [${OUT_SIZE},${IN_SIZE}]) + fi + # Benchmark config. Disable parallelism. + PERF_FLAGS="-niter 10000 -hint none -nstreams 1 -nthreads 1" + BENCH_FLAGS="-m ${MODEL_NAME} -d CPU \ + -ip ${PRECISION} ${DATA_SHAPE[@]} ${PERF_FLAGS}" + ${BENCH_RUNNER} ${BENCH_FLAGS} 2>/dev/null | \ + sed -nE "s/.*\[ INFO \]\s*Median:\s*([0-9.]+).*/\\1/p" + done +done diff --git a/tools/mlir_bench/ov_model_gen.py b/tools/mlir_bench/ov_model_gen.py new file mode 100644 index 00000000000000..f0b4fd0a7f1e28 --- /dev/null +++ b/tools/mlir_bench/ov_model_gen.py @@ -0,0 +1,247 @@ +#!/usr/bin/python3 + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations +import argparse +import string +import sys +import os + +import torch +import torch.nn as nn +import openvino as ov + + +class TorchAdd(nn.Module): + def __init__(self, sizes, type=None): + super().__init__() + # Generate random data + self.tensor = torch.empty(*sizes, dtype=type).data.normal_(0, 0.01) + def forward(self, a): + return a + self.tensor + + +class TorchSub(nn.Module): + def __init__(self, sizes, type=None): + super().__init__() + # Generate random data + self.tensor = torch.empty(*sizes, dtype=type).data.normal_(0, 0.01) + def forward(self, a): + return a - self.tensor + + +class TorchMul(nn.Module): + def __init__(self, sizes, type=None): + super().__init__() + # Generate random data + self.tensor = torch.empty(*sizes, dtype=type).data.normal_(0, 0.01) + def forward(self, a): + return a * self.tensor + + +class TorchMatmul(nn.Module): + def __init__(self, sizes_mnk, type=None): + super().__init__() + k = sizes_mnk[2] + n = sizes_mnk[1] + # Generate random data + self.weights = torch.empty(k, n, dtype=type).data.normal_(0, 0.01) + def forward(self, a): + return torch.matmul(a, self.weights) + + +class TorchDiv(nn.Module): + def __init__(self, sizes, type=None): + super().__init__() + # Generate random weights + self.tensor = torch.empty(*sizes, dtype=type).data.normal_(1, 10) + def forward(self, a): + return a / self.tensor + + +class TorchSequential(nn.Module): + def __init__(self): + super(TorchSequential, self).__init__() + self.model = nn.Sequential() + def forward(self, a): + return self.model(a) + def append(self, module: nn.Module): + self.model.append(module) + + +def get_torch_type(type: str) -> torch.dtype: + if type == 'f32': + return torch.float32 + if type == 'f16': + return torch.float16 + if type == 'bf16': + return torch.bfloat16 + assert False, f"Unsupported torch data type {type}" + + +def get_torch_layer(layer: str, sizes: list[int], type: str) -> nn.Module: + data_type = get_torch_type(type) + if layer == 'linear': + assert len(sizes) == 2, "invalid sizes for linear" + linear = nn.Linear(*sizes, dtype=data_type) + # Generate random weights + linear.weight.data.normal_(0, 0.01) + linear.bias.data.fill_(0.01) + return linear + if layer == 'relu': + return nn.ReLU() + if layer == 'add': + return TorchAdd(sizes, data_type) + if layer == 'sub': + return TorchSub(sizes, data_type) + if layer == 'mul': + return TorchMul(sizes, data_type) + if layer == 'div': + return TorchDiv(sizes, data_type) + if layer == 'matmul': + assert len(sizes) == 3, "invalid sizes for mm" + return TorchMatmul(sizes, data_type) + assert False, f"Unsupported torch layer type {layer}" + + +def get_layer_name(layer_desc: str) -> str: + return layer_desc[0:layer_desc.find('[')] + + +def get_layer_sizes(layer_desc: str) -> list[int]: + desc_sizes = layer_desc[layer_desc.find('[')+1:layer_desc.find(']')] + return [int(size) for size in filter(None, desc_sizes.split(','))] + + +def parse_layer(layer_desc: str, type: str) -> nn.Module: + layer = get_layer_name(layer_desc) + sizes = get_layer_sizes(layer_desc) + return get_torch_layer(layer, sizes, type) + + +def get_ov_type(type: str) -> ov.Type: + if type == 'f32': + return ov.Type.f32 + if type == 'f16': + return ov.Type.f16 + if type == 'bf16': + return ov.Type.bf16 + assert False, f"Unsupported OV data type {type}" + + +def get_layer_inputs(layer_desc: str, is_dynamic: bool): + input_sizes = get_layer_sizes(layer_desc) + if is_dynamic: + input_sizes = [-1] * len(input_sizes) + + layer = get_layer_name(layer_desc) + + if layer == 'matmul': + m = input_sizes[0] + k = input_sizes[2] + return [[m,k]] + + # Needs to be reversed for nn.Linear, does it apply to other layers too? + if layer == 'linear': + input_sizes.reverse() + + return [input_sizes] + + +def generate_ov_model(layers_desc: str, data_type: str, file_name: str, is_dynamic: bool = False): + layers = layers_desc.split() + torch_seq = TorchSequential() + for layer in layers: + module = parse_layer(layer, data_type) + torch_seq.append(module) + + input_sizes = get_layer_sizes(layers[0]) + if len(input_sizes) == 0: + print("Invalid input layer sizes") + sys.exit(1) + + input_shapes = get_layer_inputs(layers[0], is_dynamic) + ov_type = get_ov_type(data_type) + inputs = [(ov.PartialShape(shapes), ov_type) for shapes in input_shapes] + + ov_model = ov.convert_model(torch_seq, input=inputs) + ov.save_model(ov_model, f"{file_name}") + return ov_model + + +class BaselineMLP(nn.Module): + def __init__(self, sizes_mnk, type=None): + super(BaselineMLP, self).__init__() + m = sizes_mnk[0] + n = sizes_mnk[1] + self.bias = torch.empty((m, n), dtype=type).data.fill_(0.01) + self.relu = nn.ReLU() + def forward(self, a, b): + c = torch.matmul(a, b) + c = torch.add(c, self.bias) + return self.relu(c) + + +def baseline_MLP(model_desc: str, data_type: str, is_dynamic: bool) -> tuple[nn.Model, list]: + sizes = get_layer_sizes(model_desc) + assert len(sizes) == 3, "Invalid baseline MLP sizes" + mlp = BaselineMLP(sizes, get_torch_type(data_type)) + input_shapes = get_layer_inputs(model_desc, is_dynamic)[0] + m = input_shapes[0] + n = input_shapes[1] + k = input_shapes[2] + ov_type = get_ov_type(data_type) + inputs = [(ov.PartialShape([m, k]), ov_type), (ov.PartialShape([k, n]), ov_type)] + return (mlp, inputs) + + +def generate_baseline_model(model_desc: str, data_type: str, file_name: str, is_dynamic: bool = False): + model_name = get_layer_name(model_desc) + + if model_name == 'mlp': + baseline_tuple = baseline_MLP(model_desc, data_type, is_dynamic) + else: + assert False, f"Unsupported baseline model data type {model_name}" + + ov_model = ov.convert_model(baseline_tuple[0], input=baseline_tuple[1]) + ov.save_model(ov_model, f"{file_name}") + return ov_model + + +def main(): + parser = argparse.ArgumentParser( + prog='OV Model generator', + description='Generate PyTorch model and export as OV .xml') + parser.add_argument('-l', '--layers', type=str.lower, + help='Model layers description. For example:\ + -l="linear[64,32] relu[] linear[32,16] gelu[]"\ + -l="matmul[128,128,1024] add[128,128] relu[]"\ + -l="add[8,8] div[8,8]"') + parser.add_argument('-t', '--type', default='f32', type=str.lower, + help='Data type: f32|f16|bf16|...') + parser.add_argument('--dynamic', action='store_true', + help='Make model shapes dynamic') + parser.add_argument('-n', '--name', default='temp.xml', + help='Name for exported XML model') + parser.add_argument('-b', '--baseline', default=None, type=str.lower, + help='Baseline pre-made model - overrides layers. For example:\ + -b=mlp[32,64,16]') + parser.add_argument('-p', '--print', action='store_true', + help='Compile and print the model') + args = parser.parse_args() + + if args.baseline is not None: + model = generate_baseline_model(args.baseline, args.type, args.name, args.dynamic) + else: + model = generate_ov_model(args.layers, args.type, args.name, args.dynamic) + + if args.print: + ov.compile_model(model, 'CPU') + + return 0 + + +if __name__ == '__main__': + os._exit(main()) diff --git a/tools/mlir_bench/ov_raw_mlir_bench.sh b/tools/mlir_bench/ov_raw_mlir_bench.sh new file mode 100755 index 00000000000000..9f12cb50b35d16 --- /dev/null +++ b/tools/mlir_bench/ov_raw_mlir_bench.sh @@ -0,0 +1,101 @@ +#!/bin/bash + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +# Runs pure MLIR part of MLP benchmarks using TPP-MLIR. +# This approach assumes that only one MLIR op is generated. +# For example, the whole graph is outlined to MLIR. + +die_syntax() { + echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-b (mlp)] [-D]" + echo "" + echo " -t: Optional data type" + echo " -b: Optional baseline model" + echo " -D: Set model shapes to dynamic" + exit 1 +} + +# Cmd-line opts +while getopts "t:b:D" arg; do + case ${arg} in + t) + DATA_TYPE=${OPTARG} + ;; + b) + BASELINE_MODEL=${OPTARG} + ;; + D) + IS_DYNAMIC=true + ;; + ?) + echo "Invalid option: ${OPTARG}" + die_syntax + ;; + esac +done + +OV_ROOT=$(git rev-parse --show-toplevel) +BENCH_ROOT=$(realpath ${OV_ROOT}/tools/mlir_bench) + +MODEL_GEN=$(realpath ${BENCH_ROOT}/ov_model_gen.py) +BENCH_RUNNER=tpp-run + +# Initial validation. +if ! [ -d ${OV_ROOT} ]; then + echo "Missing OV repo" + exit 1 +fi +if ! [ -d ${BENCH_ROOT} ]; then + echo "Missing MLIR benchmark directory" + exit 1 +fi +if ! [ -f ${MODEL_GEN} ]; then + echo "Missing model generator" + exit 1 +fi +if ! [ "$(command -v ${BENCH_RUNNER})" ]; then + echo "Missing benchmark runner ${BENCH_RUNNER}" + exit 1 +fi +if [ ${IS_DYNAMIC} ]; then + echo "Dynamic shapes are not supported by ${BENCH_RUNNER}" + exit 1 +fi + +# Kernel config. +INPUT_SIZES=( 1024 2048 4096 8192 ) +OUTPUT_SIZES=( 128 256 512 ) +if [ ! "${DATA_TYPE}" ]; then + DATA_TYPE="f32" +fi +MODEL_NAME="TPP_BENCH.xml" + +echo "Result type: time [ns]" +for OUT_SIZE in "${OUTPUT_SIZES[@]}"; do + echo "MLP - OUT: ${OUT_SIZE} INS: ${INPUT_SIZES[@]}" + for IN_SIZE in "${INPUT_SIZES[@]}"; do + # Generate model. + if [ "${BASELINE_MODEL}" ]; then + # Enable baseline model flag. + MODEL_CONFIG=(-b="${BASELINE_MODEL}[${OUT_SIZE},${OUT_SIZE},${IN_SIZE}]") + else + # Generate default PyTorch MLP. + MODEL_CONFIG=(-l="linear[${IN_SIZE},${OUT_SIZE}] relu[]") + fi + GEN_FLAGS=(-t ${DATA_TYPE} -n ${MODEL_NAME}) + GEN_FLAGS+=(-p) + ENV_FLAGS=OV_MLIR_TPP=0 + MODEL_OUT=$(exec env ${ENV_FLAGS} python3 ${MODEL_GEN} "${MODEL_CONFIG[@]}" "${GEN_FLAGS[@]}" 2>&1) + if [ $? != 0 ]; then + echo "Failed to generate model" + exit 1 + fi + # Run benchmark. + MLIR_IR=$(echo "${MODEL_OUT}" \ + | awk '/Source MLIR:/{flag=1; next} /Target LLVM:/{flag=0} flag' \ + | grep -vE '^[-]+$') + BENCH_FLAGS="-entry-point-result=void -e entry -seed 123 -n 10000" + echo "${MLIR_IR}" | ${BENCH_RUNNER} ${BENCH_FLAGS} + done +done diff --git a/tools/mlir_bench/tpp_mlir_bench.sh b/tools/mlir_bench/tpp_mlir_bench.sh new file mode 100755 index 00000000000000..fbecd29cd80317 --- /dev/null +++ b/tools/mlir_bench/tpp_mlir_bench.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +# Runs MLIR only MLP benchmarks using TPP-MLIR. + +die_syntax() { + echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-D]" + echo "" + echo " -t: Optional data type" + echo " -D: Set model shapes to dynamic" + exit 1 +} + +# Cmd-line opts +while getopts "t:D" arg; do + case ${arg} in + t) + DATA_TYPE=${OPTARG} + ;; + D) + IS_DYNAMIC=true + ;; + ?) + echo "Invalid option: ${OPTARG}" + die_syntax + ;; + esac +done + +MODEL_GEN=mlir-gen +BENCH_RUNNER=tpp-run + +# Initial validation. +if ! [ "$(command -v ${MODEL_GEN})" ]; then + echo "Missing model generator ${MODEL_GEN}" + exit 1 +fi +if ! [ "$(command -v ${BENCH_RUNNER})" ]; then + echo "Missing benchmark runner ${BENCH_RUNNER}" + exit 1 +fi +if [ ${IS_DYNAMIC} ]; then + echo "Dynamic shapes are not supported by ${BENCH_RUNNER}" + exit 1 +fi + +# Kernel config. +INPUT_SIZES=( 1024 2048 4096 8192 ) +OUTPUT_SIZES=( 128 256 512 ) +if [ ! "${DATA_TYPE}" ]; then + DATA_TYPE="f32" +fi + +echo "Result type: time [ns]" +for OUT_SIZE in "${OUTPUT_SIZES[@]}"; do + echo "MLP - OUT: ${OUT_SIZE} INS: ${INPUT_SIZES[@]}" + for IN_SIZE in "${INPUT_SIZES[@]}"; do + # Generate model. + if [ "${BASELINE_MODEL}" ]; then + # Enable baseline model flag. + MODEL_CONFIG=(-b="${BASELINE_MODEL}[${OUT_SIZE},${OUT_SIZE},${IN_SIZE}]") + else + # Generate default PyTorch MLP. + MODEL_CONFIG=(-l="linear[${IN_SIZE},${OUT_SIZE}] relu[]") + fi + MODEL_CONFIG=(--batch=${OUT_SIZE} --layers=${IN_SIZE},${OUT_SIZE} -bias -relu) + GEN_FLAGS=(--kernel=args --float-type=${DATA_TYPE} --seed=123) + MLIR_IR=$(${MODEL_GEN} "${MODEL_CONFIG[@]}" "${GEN_FLAGS[@]}") + if [ $? != 0 ]; then + echo "Failed to generate model" + exit 1 + fi + # Run benchmark. + BENCH_FLAGS="-entry-point-result=void -e entry -seed 123 -n 10000" + echo "${MLIR_IR}" | ${BENCH_RUNNER} ${BENCH_FLAGS} + done +done From 1f9a555aca3bfa646ba39cc2c361f0259ff1f05c Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Tue, 30 Jul 2024 22:29:34 +0200 Subject: [PATCH 024/121] Align MLP sizes with TPP-MLIR benchmarks (#158) Resizes MLP benchmarks to align with existing MLIR tests. Tweaks model generator to expose full control over M,N,K dimensions of torch Linear layer. --- tools/mlir_bench/libxsmm_bench.sh | 16 ++++++------- tools/mlir_bench/mlp_bench.sh | 33 +++++++++++++-------------- tools/mlir_bench/ov_model_gen.py | 25 +++++++++----------- tools/mlir_bench/ov_raw_mlir_bench.sh | 14 ++++++------ tools/mlir_bench/tpp_mlir_bench.sh | 19 +++++---------- 5 files changed, 48 insertions(+), 59 deletions(-) diff --git a/tools/mlir_bench/libxsmm_bench.sh b/tools/mlir_bench/libxsmm_bench.sh index b39da1d187ceae..6852cedab0edd5 100755 --- a/tools/mlir_bench/libxsmm_bench.sh +++ b/tools/mlir_bench/libxsmm_bench.sh @@ -36,22 +36,22 @@ if ! [ "$(command -v ${BENCH_RUNNER})" ]; then echo "Missing benchmark runner ${BENCH_RUNNER}" exit 1 fi -if [ ${IS_DYNAMIC} ]; then +if [ "${IS_DYNAMIC}" ]; then echo "Dynamic shapes are not supported by ${BENCH_RUNNER}" exit 1 fi # Kernel config. -INPUT_SIZES=( 1024 2048 4096 8192 ) -OUTPUT_SIZES=( 128 256 512 ) +LAYERS=( 1024 2048 4096 8192 ) +MINI_BATCHES=( 128 256 512 ) if [ ! "${DATA_TYPE}" ]; then DATA_TYPE="f32" fi echo "Result type: GFLOPS" -for OUT_SIZE in "${OUTPUT_SIZES[@]}"; do - echo "MLP - OUT: ${OUT_SIZE} INS: ${INPUT_SIZES[@]}" - for IN_SIZE in "${INPUT_SIZES[@]}"; do +for MB in "${MINI_BATCHES[@]}"; do + echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" + for LAYER in "${LAYERS[@]}"; do # Run benchmark. NUM_ITER=10000 FUSE_TYPE=5 @@ -63,8 +63,8 @@ for OUT_SIZE in "${OUTPUT_SIZES[@]}"; do fi # Disable parallelism. ENV_FLAGS=OMP_NUM_THREADS=1 - exec env ${ENV_FLAGS} ${BENCH_RUNNER} ${NUM_ITER} ${OUT_SIZE} ${FUSE_TYPE} ${TYPE} ${TILES[@]} \ - ${LAYOUT[@]} ${IN_SIZE} ${OUT_SIZE} \ + exec env ${ENV_FLAGS} ${BENCH_RUNNER} ${NUM_ITER} ${MB} ${FUSE_TYPE} ${TYPE} ${TILES[@]} \ + ${LAYOUT[@]} ${LAYER} ${LAYER} \ | sed -nE "s/.*GFLOPS\s+=\s*([0-9.]+).*/\\1/p" done done diff --git a/tools/mlir_bench/mlp_bench.sh b/tools/mlir_bench/mlp_bench.sh index ca52ee995d01cd..92fb753ea4a040 100755 --- a/tools/mlir_bench/mlp_bench.sh +++ b/tools/mlir_bench/mlp_bench.sh @@ -34,21 +34,21 @@ while getopts "t:b:D" arg; do done OV_ROOT=$(git rev-parse --show-toplevel) -BENCH_ROOT=$(realpath ${OV_ROOT}/tools/mlir_bench) +BENCH_ROOT=$(realpath "${OV_ROOT}/tools/mlir_bench") -MODEL_GEN=$(realpath ${BENCH_ROOT}/ov_model_gen.py) +MODEL_GEN=$(realpath "${BENCH_ROOT}/ov_model_gen.py") BENCH_RUNNER=benchmark_app # Initial validation. -if ! [ -d ${OV_ROOT} ]; then +if ! [ -d "${OV_ROOT}" ]; then echo "Missing OV repo" exit 1 fi -if ! [ -d ${BENCH_ROOT} ]; then +if ! [ -d "${BENCH_ROOT}" ]; then echo "Missing MLIR benchmark directory" exit 1 fi -if ! [ -f ${MODEL_GEN} ]; then +if ! [ -f "${MODEL_GEN}" ]; then echo "Missing model generator" exit 1 fi @@ -56,30 +56,30 @@ if ! [ "$(command -v ${BENCH_RUNNER})" ]; then echo "Missing benchmark runner ${BENCH_RUNNER}" exit 1 fi -if [ "${BASELINE_MODEL}" ] && [ ${IS_DYNAMIC} ]; then +if [ "${BASELINE_MODEL}" ] && [ "${IS_DYNAMIC}" ]; then echo "Baseline models with dynamic shapes not supported" exit 1 fi # Kernel config. -INPUT_SIZES=( 1024 2048 4096 8192 ) -OUTPUT_SIZES=( 128 256 512 ) +LAYERS=( 1024 2048 4096 8192 ) +MINI_BATCHES=( 128 256 512 ) if [ ! "${DATA_TYPE}" ]; then DATA_TYPE="f32" fi MODEL_NAME="MLIR_MLP_BENCH.xml" echo "Result type: time [ms]" -for OUT_SIZE in "${OUTPUT_SIZES[@]}"; do - echo "MLP - OUT: ${OUT_SIZE} INS: ${INPUT_SIZES[@]}" - for IN_SIZE in "${INPUT_SIZES[@]}"; do +for MB in "${MINI_BATCHES[@]}"; do + echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" + for LAYER in "${LAYERS[@]}"; do # Generate model. if [ "${BASELINE_MODEL}" ]; then # Enable baseline model flag. - MODEL_CONFIG=(-b="${BASELINE_MODEL}[${OUT_SIZE},${OUT_SIZE},${IN_SIZE}]") + MODEL_CONFIG=(-b="${BASELINE_MODEL}[${MB},${LAYER},${LAYER}]") else # Generate default PyTorch MLP. - MODEL_CONFIG=(-l="linear[${IN_SIZE},${OUT_SIZE}] relu[]") + MODEL_CONFIG=(-l="linear[${MB},${LAYER},${LAYER}] relu[]") fi GEN_FLAGS=(-t ${DATA_TYPE} -n ${MODEL_NAME}) if [ ${IS_DYNAMIC} ]; then @@ -96,13 +96,12 @@ for OUT_SIZE in "${OUTPUT_SIZES[@]}"; do # No native support for bf16, use simple f16 instead. PRECISION="f16" fi - if [ ${IS_DYNAMIC} ]; then - DATA_SHAPE=(-data_shape [${OUT_SIZE},${IN_SIZE}]) + if [ "${IS_DYNAMIC}" ]; then + DATA_SHAPE=(-data_shape [${MB},${LAYER}]) fi # Benchmark config. Disable parallelism. PERF_FLAGS="-niter 10000 -hint none -nstreams 1 -nthreads 1" - BENCH_FLAGS="-m ${MODEL_NAME} -d CPU \ - -ip ${PRECISION} ${DATA_SHAPE[@]} ${PERF_FLAGS}" + BENCH_FLAGS="-m ${MODEL_NAME} -d CPU -ip ${PRECISION} ${DATA_SHAPE[@]} ${PERF_FLAGS}" ${BENCH_RUNNER} ${BENCH_FLAGS} 2>/dev/null | \ sed -nE "s/.*\[ INFO \]\s*Median:\s*([0-9.]+).*/\\1/p" done diff --git a/tools/mlir_bench/ov_model_gen.py b/tools/mlir_bench/ov_model_gen.py index f0b4fd0a7f1e28..56e0e82ce81618 100644 --- a/tools/mlir_bench/ov_model_gen.py +++ b/tools/mlir_bench/ov_model_gen.py @@ -84,8 +84,8 @@ def get_torch_type(type: str) -> torch.dtype: def get_torch_layer(layer: str, sizes: list[int], type: str) -> nn.Module: data_type = get_torch_type(type) if layer == 'linear': - assert len(sizes) == 2, "invalid sizes for linear" - linear = nn.Linear(*sizes, dtype=data_type) + assert len(sizes) == 3, "invalid sizes for linear - expects [m,n,k]" + linear = nn.Linear(sizes[2], sizes[1], dtype=data_type) # Generate random weights linear.weight.data.normal_(0, 0.01) linear.bias.data.fill_(0.01) @@ -101,7 +101,7 @@ def get_torch_layer(layer: str, sizes: list[int], type: str) -> nn.Module: if layer == 'div': return TorchDiv(sizes, data_type) if layer == 'matmul': - assert len(sizes) == 3, "invalid sizes for mm" + assert len(sizes) == 3, "invalid sizes for mm - expects [m,n,k]" return TorchMatmul(sizes, data_type) assert False, f"Unsupported torch layer type {layer}" @@ -138,19 +138,16 @@ def get_layer_inputs(layer_desc: str, is_dynamic: bool): layer = get_layer_name(layer_desc) - if layer == 'matmul': + if layer == 'matmul' or layer == 'linear': m = input_sizes[0] k = input_sizes[2] - return [[m,k]] - - # Needs to be reversed for nn.Linear, does it apply to other layers too? - if layer == 'linear': - input_sizes.reverse() + return [m,k] - return [input_sizes] + return input_sizes -def generate_ov_model(layers_desc: str, data_type: str, file_name: str, is_dynamic: bool = False): +def generate_ov_model(layers_desc: str, data_type: str, file_name: str, + is_dynamic: bool = False): layers = layers_desc.split() torch_seq = TorchSequential() for layer in layers: @@ -164,7 +161,7 @@ def generate_ov_model(layers_desc: str, data_type: str, file_name: str, is_dynam input_shapes = get_layer_inputs(layers[0], is_dynamic) ov_type = get_ov_type(data_type) - inputs = [(ov.PartialShape(shapes), ov_type) for shapes in input_shapes] + inputs = (ov.PartialShape(input_shapes), ov_type) ov_model = ov.convert_model(torch_seq, input=inputs) ov.save_model(ov_model, f"{file_name}") @@ -188,7 +185,7 @@ def baseline_MLP(model_desc: str, data_type: str, is_dynamic: bool) -> tuple[nn. sizes = get_layer_sizes(model_desc) assert len(sizes) == 3, "Invalid baseline MLP sizes" mlp = BaselineMLP(sizes, get_torch_type(data_type)) - input_shapes = get_layer_inputs(model_desc, is_dynamic)[0] + input_shapes = get_layer_inputs(model_desc, is_dynamic) m = input_shapes[0] n = input_shapes[1] k = input_shapes[2] @@ -216,7 +213,7 @@ def main(): description='Generate PyTorch model and export as OV .xml') parser.add_argument('-l', '--layers', type=str.lower, help='Model layers description. For example:\ - -l="linear[64,32] relu[] linear[32,16] gelu[]"\ + -l="linear[128,1024,256] relu[] linear[128,512,1024] gelu[]"\ -l="matmul[128,128,1024] add[128,128] relu[]"\ -l="add[8,8] div[8,8]"') parser.add_argument('-t', '--type', default='f32', type=str.lower, diff --git a/tools/mlir_bench/ov_raw_mlir_bench.sh b/tools/mlir_bench/ov_raw_mlir_bench.sh index 9f12cb50b35d16..33cae52f0435fa 100755 --- a/tools/mlir_bench/ov_raw_mlir_bench.sh +++ b/tools/mlir_bench/ov_raw_mlir_bench.sh @@ -64,24 +64,24 @@ if [ ${IS_DYNAMIC} ]; then fi # Kernel config. -INPUT_SIZES=( 1024 2048 4096 8192 ) -OUTPUT_SIZES=( 128 256 512 ) +LAYERS=( 1024 2048 4096 8192 ) +MINI_BATCHES=( 128 256 512 ) if [ ! "${DATA_TYPE}" ]; then DATA_TYPE="f32" fi MODEL_NAME="TPP_BENCH.xml" echo "Result type: time [ns]" -for OUT_SIZE in "${OUTPUT_SIZES[@]}"; do - echo "MLP - OUT: ${OUT_SIZE} INS: ${INPUT_SIZES[@]}" - for IN_SIZE in "${INPUT_SIZES[@]}"; do +for MB in "${MINI_BATCHES[@]}"; do + echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" + for LAYER in "${LAYERS[@]}"; do # Generate model. if [ "${BASELINE_MODEL}" ]; then # Enable baseline model flag. - MODEL_CONFIG=(-b="${BASELINE_MODEL}[${OUT_SIZE},${OUT_SIZE},${IN_SIZE}]") + MODEL_CONFIG=(-b="${BASELINE_MODEL}[${MB},${LAYER},${LAYER}]") else # Generate default PyTorch MLP. - MODEL_CONFIG=(-l="linear[${IN_SIZE},${OUT_SIZE}] relu[]") + MODEL_CONFIG=(-l="linear[${MB},${LAYER},${LAYER}] relu[]") fi GEN_FLAGS=(-t ${DATA_TYPE} -n ${MODEL_NAME}) GEN_FLAGS+=(-p) diff --git a/tools/mlir_bench/tpp_mlir_bench.sh b/tools/mlir_bench/tpp_mlir_bench.sh index fbecd29cd80317..45bf82dab30939 100755 --- a/tools/mlir_bench/tpp_mlir_bench.sh +++ b/tools/mlir_bench/tpp_mlir_bench.sh @@ -47,25 +47,18 @@ if [ ${IS_DYNAMIC} ]; then fi # Kernel config. -INPUT_SIZES=( 1024 2048 4096 8192 ) -OUTPUT_SIZES=( 128 256 512 ) +LAYERS=( 1024 2048 4096 8192 ) +MINI_BATCHES=( 128 256 512 ) if [ ! "${DATA_TYPE}" ]; then DATA_TYPE="f32" fi echo "Result type: time [ns]" -for OUT_SIZE in "${OUTPUT_SIZES[@]}"; do - echo "MLP - OUT: ${OUT_SIZE} INS: ${INPUT_SIZES[@]}" - for IN_SIZE in "${INPUT_SIZES[@]}"; do +for MB in "${MINI_BATCHES[@]}"; do + echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" + for LAYER in "${LAYERS[@]}"; do # Generate model. - if [ "${BASELINE_MODEL}" ]; then - # Enable baseline model flag. - MODEL_CONFIG=(-b="${BASELINE_MODEL}[${OUT_SIZE},${OUT_SIZE},${IN_SIZE}]") - else - # Generate default PyTorch MLP. - MODEL_CONFIG=(-l="linear[${IN_SIZE},${OUT_SIZE}] relu[]") - fi - MODEL_CONFIG=(--batch=${OUT_SIZE} --layers=${IN_SIZE},${OUT_SIZE} -bias -relu) + MODEL_CONFIG=(--batch=${MB} --layers=${LAYER},${LAYER} -bias -relu) GEN_FLAGS=(--kernel=args --float-type=${DATA_TYPE} --seed=123) MLIR_IR=$(${MODEL_GEN} "${MODEL_CONFIG[@]}" "${GEN_FLAGS[@]}") if [ $? != 0 ]; then From 80dd502234e4ff409feb99109f170a621305e2f3 Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Tue, 30 Jul 2024 22:35:53 +0200 Subject: [PATCH 025/121] Lower number of benchmark iterations (#159) Fewer iterations to speedup testing. Can be changed/reverted later. --- tools/mlir_bench/libxsmm_bench.sh | 2 +- tools/mlir_bench/mlp_bench.sh | 2 +- tools/mlir_bench/ov_raw_mlir_bench.sh | 2 +- tools/mlir_bench/tpp_mlir_bench.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/mlir_bench/libxsmm_bench.sh b/tools/mlir_bench/libxsmm_bench.sh index 6852cedab0edd5..c4d43a545e3ec5 100755 --- a/tools/mlir_bench/libxsmm_bench.sh +++ b/tools/mlir_bench/libxsmm_bench.sh @@ -53,7 +53,7 @@ for MB in "${MINI_BATCHES[@]}"; do echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" for LAYER in "${LAYERS[@]}"; do # Run benchmark. - NUM_ITER=10000 + NUM_ITER=1000 FUSE_TYPE=5 TYPE=F TILES=(64 64 64) diff --git a/tools/mlir_bench/mlp_bench.sh b/tools/mlir_bench/mlp_bench.sh index 92fb753ea4a040..cd5779dbfefdea 100755 --- a/tools/mlir_bench/mlp_bench.sh +++ b/tools/mlir_bench/mlp_bench.sh @@ -100,7 +100,7 @@ for MB in "${MINI_BATCHES[@]}"; do DATA_SHAPE=(-data_shape [${MB},${LAYER}]) fi # Benchmark config. Disable parallelism. - PERF_FLAGS="-niter 10000 -hint none -nstreams 1 -nthreads 1" + PERF_FLAGS="-niter 1000 -hint none -nstreams 1 -nthreads 1" BENCH_FLAGS="-m ${MODEL_NAME} -d CPU -ip ${PRECISION} ${DATA_SHAPE[@]} ${PERF_FLAGS}" ${BENCH_RUNNER} ${BENCH_FLAGS} 2>/dev/null | \ sed -nE "s/.*\[ INFO \]\s*Median:\s*([0-9.]+).*/\\1/p" diff --git a/tools/mlir_bench/ov_raw_mlir_bench.sh b/tools/mlir_bench/ov_raw_mlir_bench.sh index 33cae52f0435fa..c81c41f2150f7b 100755 --- a/tools/mlir_bench/ov_raw_mlir_bench.sh +++ b/tools/mlir_bench/ov_raw_mlir_bench.sh @@ -95,7 +95,7 @@ for MB in "${MINI_BATCHES[@]}"; do MLIR_IR=$(echo "${MODEL_OUT}" \ | awk '/Source MLIR:/{flag=1; next} /Target LLVM:/{flag=0} flag' \ | grep -vE '^[-]+$') - BENCH_FLAGS="-entry-point-result=void -e entry -seed 123 -n 10000" + BENCH_FLAGS="-entry-point-result=void -e entry -seed 123 -n 1000" echo "${MLIR_IR}" | ${BENCH_RUNNER} ${BENCH_FLAGS} done done diff --git a/tools/mlir_bench/tpp_mlir_bench.sh b/tools/mlir_bench/tpp_mlir_bench.sh index 45bf82dab30939..28df0d1969f96d 100755 --- a/tools/mlir_bench/tpp_mlir_bench.sh +++ b/tools/mlir_bench/tpp_mlir_bench.sh @@ -66,7 +66,7 @@ for MB in "${MINI_BATCHES[@]}"; do exit 1 fi # Run benchmark. - BENCH_FLAGS="-entry-point-result=void -e entry -seed 123 -n 10000" + BENCH_FLAGS="-entry-point-result=void -e entry -seed 123 -n 1000" echo "${MLIR_IR}" | ${BENCH_RUNNER} ${BENCH_FLAGS} done done From 77f5ea3de5d52f263c090c40444d896cc9bd61ce Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Wed, 31 Jul 2024 15:01:46 +0200 Subject: [PATCH 026/121] MLP bench - control OV infer precision (#160) Sets OV infer precision same as the data type. Minor debug print and bash fixes. --- tools/mlir_bench/mlp_bench.sh | 4 ++-- tools/mlir_bench/ov_raw_mlir_bench.sh | 4 ++-- tools/mlir_bench/tpp_mlir_bench.sh | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/mlir_bench/mlp_bench.sh b/tools/mlir_bench/mlp_bench.sh index cd5779dbfefdea..b9253549f42eef 100755 --- a/tools/mlir_bench/mlp_bench.sh +++ b/tools/mlir_bench/mlp_bench.sh @@ -82,7 +82,7 @@ for MB in "${MINI_BATCHES[@]}"; do MODEL_CONFIG=(-l="linear[${MB},${LAYER},${LAYER}] relu[]") fi GEN_FLAGS=(-t ${DATA_TYPE} -n ${MODEL_NAME}) - if [ ${IS_DYNAMIC} ]; then + if [ "${IS_DYNAMIC}" ]; then GEN_FLAGS+=(--dynamic) fi python3 ${MODEL_GEN} "${MODEL_CONFIG[@]}" "${GEN_FLAGS[@]}" @@ -101,7 +101,7 @@ for MB in "${MINI_BATCHES[@]}"; do fi # Benchmark config. Disable parallelism. PERF_FLAGS="-niter 1000 -hint none -nstreams 1 -nthreads 1" - BENCH_FLAGS="-m ${MODEL_NAME} -d CPU -ip ${PRECISION} ${DATA_SHAPE[@]} ${PERF_FLAGS}" + BENCH_FLAGS="-m ${MODEL_NAME} -d CPU -ip ${PRECISION} -infer_precision ${DATA_TYPE} ${DATA_SHAPE[@]} ${PERF_FLAGS}" ${BENCH_RUNNER} ${BENCH_FLAGS} 2>/dev/null | \ sed -nE "s/.*\[ INFO \]\s*Median:\s*([0-9.]+).*/\\1/p" done diff --git a/tools/mlir_bench/ov_raw_mlir_bench.sh b/tools/mlir_bench/ov_raw_mlir_bench.sh index c81c41f2150f7b..2d6a786f349410 100755 --- a/tools/mlir_bench/ov_raw_mlir_bench.sh +++ b/tools/mlir_bench/ov_raw_mlir_bench.sh @@ -58,7 +58,7 @@ if ! [ "$(command -v ${BENCH_RUNNER})" ]; then echo "Missing benchmark runner ${BENCH_RUNNER}" exit 1 fi -if [ ${IS_DYNAMIC} ]; then +if [ "${IS_DYNAMIC}" ]; then echo "Dynamic shapes are not supported by ${BENCH_RUNNER}" exit 1 fi @@ -71,7 +71,7 @@ if [ ! "${DATA_TYPE}" ]; then fi MODEL_NAME="TPP_BENCH.xml" -echo "Result type: time [ns]" +echo "Result type: time [s]" for MB in "${MINI_BATCHES[@]}"; do echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" for LAYER in "${LAYERS[@]}"; do diff --git a/tools/mlir_bench/tpp_mlir_bench.sh b/tools/mlir_bench/tpp_mlir_bench.sh index 28df0d1969f96d..0a3c2520fce72e 100755 --- a/tools/mlir_bench/tpp_mlir_bench.sh +++ b/tools/mlir_bench/tpp_mlir_bench.sh @@ -41,7 +41,7 @@ if ! [ "$(command -v ${BENCH_RUNNER})" ]; then echo "Missing benchmark runner ${BENCH_RUNNER}" exit 1 fi -if [ ${IS_DYNAMIC} ]; then +if [ "${IS_DYNAMIC}" ]; then echo "Dynamic shapes are not supported by ${BENCH_RUNNER}" exit 1 fi @@ -53,7 +53,7 @@ if [ ! "${DATA_TYPE}" ]; then DATA_TYPE="f32" fi -echo "Result type: time [ns]" +echo "Result type: time [s]" for MB in "${MINI_BATCHES[@]}"; do echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" for LAYER in "${LAYERS[@]}"; do From e93f1a66bf9a691e9ea5b556044d76cdb05dbb7e Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Wed, 31 Jul 2024 19:23:05 +0200 Subject: [PATCH 027/121] TPP const weights and benchmark runners (#161) Adds option for TPP-MLIR benchmark with weights as constants which enables compile-time packing. Also, add utility benchmark runners. --- tools/mlir_bench/run_bench_bf16.sh | 28 ++++++++++++++++++++++++++++ tools/mlir_bench/run_bench_f32.sh | 28 ++++++++++++++++++++++++++++ tools/mlir_bench/tpp_mlir_bench.sh | 14 +++++++++++--- 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100755 tools/mlir_bench/run_bench_bf16.sh create mode 100755 tools/mlir_bench/run_bench_f32.sh diff --git a/tools/mlir_bench/run_bench_bf16.sh b/tools/mlir_bench/run_bench_bf16.sh new file mode 100755 index 00000000000000..f6d031d29a5222 --- /dev/null +++ b/tools/mlir_bench/run_bench_bf16.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +echo "### MLP BF16 benchmarks ###" +echo "LIBXSMM" +../tools/mlir_bench/libxsmm_bench.sh -B +echo "TPP-MLIR args weights" +../tools/mlir_bench/tpp_mlir_bench.sh -t bf16 + +echo "" +echo "Baseline MLP" +echo "OV - no MLIR - baseline model" +OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t bf16 -b mlp +echo "OV + MLIR - kernel only - baseline model" +../tools/mlir_bench/ov_raw_mlir_bench.sh -t bf16 -b mlp +echo "OV + MLIR - full - baseline model" +OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t bf16 -b mlp + +echo "" +echo "PyTorch MLP" +echo "OV - no MLIR - PyTorch" +OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t bf16 +echo "OV + MLIR - kernel only - PyTorch" +../tools/mlir_bench/ov_raw_mlir_bench.sh -t bf16 +echo "OV + MLIR - full - PyTorch" +OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t bf16 + +echo "TPP-MLIR const weights" +../tools/mlir_bench/tpp_mlir_bench.sh -t bf16 -C diff --git a/tools/mlir_bench/run_bench_f32.sh b/tools/mlir_bench/run_bench_f32.sh new file mode 100755 index 00000000000000..de7de3fbf50695 --- /dev/null +++ b/tools/mlir_bench/run_bench_f32.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +echo "### MLP F32 benchmarks ###" +echo "LIBXSMM" +../tools/mlir_bench/libxsmm_bench.sh +echo "TPP-MLIR args weights" +../tools/mlir_bench/tpp_mlir_bench.sh -t f32 + +echo "" +echo "Baseline MLP" +echo "OV - no MLIR - baseline model" +OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t f32 -b mlp +echo "OV + MLIR - kernel only - baseline model" +../tools/mlir_bench/ov_raw_mlir_bench.sh -t f32 -b mlp +echo "OV + MLIR - full - baseline model" +OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t f32 -b mlp + +echo "" +echo "PyTorch MLP" +echo "OV - no MLIR - PyTorch" +OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t f32 +echo "OV + MLIR - kernel only - PyTorch" +../tools/mlir_bench/ov_raw_mlir_bench.sh -t f32 +echo "OV + MLIR - full - PyTorch" +OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t f32 + +echo "TPP-MLIR const weights" +../tools/mlir_bench/tpp_mlir_bench.sh -t f32 -C diff --git a/tools/mlir_bench/tpp_mlir_bench.sh b/tools/mlir_bench/tpp_mlir_bench.sh index 0a3c2520fce72e..6f07340a08333c 100755 --- a/tools/mlir_bench/tpp_mlir_bench.sh +++ b/tools/mlir_bench/tpp_mlir_bench.sh @@ -6,15 +6,16 @@ # Runs MLIR only MLP benchmarks using TPP-MLIR. die_syntax() { - echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-D]" + echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-D] [-C]" echo "" echo " -t: Optional data type" echo " -D: Set model shapes to dynamic" + echo " -C: Weights as constants (default: arguments)" exit 1 } # Cmd-line opts -while getopts "t:D" arg; do +while getopts "t:DC" arg; do case ${arg} in t) DATA_TYPE=${OPTARG} @@ -22,6 +23,9 @@ while getopts "t:D" arg; do D) IS_DYNAMIC=true ;; + C) + CONST_WEIGHTS=true + ;; ?) echo "Invalid option: ${OPTARG}" die_syntax @@ -59,7 +63,11 @@ for MB in "${MINI_BATCHES[@]}"; do for LAYER in "${LAYERS[@]}"; do # Generate model. MODEL_CONFIG=(--batch=${MB} --layers=${LAYER},${LAYER} -bias -relu) - GEN_FLAGS=(--kernel=args --float-type=${DATA_TYPE} --seed=123) + KERNEL_TYPE=args + if [ "${CONST_WEIGHTS}" ]; then + KERNEL_TYPE=const + fi + GEN_FLAGS=(--kernel=${KERNEL_TYPE} --float-type=${DATA_TYPE} --seed=123) MLIR_IR=$(${MODEL_GEN} "${MODEL_CONFIG[@]}" "${GEN_FLAGS[@]}") if [ $? != 0 ]; then echo "Failed to generate model" From aa86cfbd6c48e0bf850c994439a7bd4886521b3f Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Thu, 1 Aug 2024 14:30:08 +0200 Subject: [PATCH 028/121] Integration with GraphCompiler (#155) * Integration with GraphCompiler Grapth Compiler is disabled by default, to enable build with -DENABLE_GRAPH_COMPILER=ON * Add suggestions from code review * Apply suggestions from code review Co-authored-by: Sergey Lyalin --------- Co-authored-by: Sergey Lyalin --- CMakeLists.txt | 8 + cmake/features.cmake | 2 + cmake/graph-compiler.cmake | 31 ++++ src/cmake/openvino.cmake | 1 + src/common/transformations/CMakeLists.txt | 9 +- .../src/transformations/mlir/convert.cpp | 136 +++++++++------ .../src/transformations/mlir/mlir_op.cpp | 157 ++++++++++-------- .../src/transformations/mlir/mlir_op.hpp | 9 +- 8 files changed, 231 insertions(+), 122 deletions(-) create mode 100644 cmake/graph-compiler.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 4690f6b8976bf2..8a08435f387a3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,6 +150,14 @@ endfunction() # FIXME: Move to all-upsteram lowering into XSMM/DNN/MKL include(cmake/tpp-mlir.cmake) +# +# Graph Compiler +# +if (ENABLE_GRAPH_COMPILER) + include(cmake/graph-compiler.cmake) + add_definitions(-DGRAPH_COMPILER) +endif() + # # Build # diff --git a/cmake/features.cmake b/cmake/features.cmake index 1adbadaa23c1aa..4597dc0e3b5230 100644 --- a/cmake/features.cmake +++ b/cmake/features.cmake @@ -45,6 +45,8 @@ ov_dependent_option (ENABLE_ONEDNN_FOR_GPU "Enable oneDNN with GPU support" ${EN ov_dependent_option (ENABLE_INTEL_NPU "NPU plugin for OpenVINO runtime" ON "X86_64;WIN32 OR LINUX" OFF) ov_dependent_option (ENABLE_INTEL_NPU_INTERNAL "NPU plugin internal components for OpenVINO runtime" ON "ENABLE_INTEL_NPU" OFF) +ov_option (ENABLE_GRAPH_COMPILER "Enable Graph Compiler" OFF) + ov_option (ENABLE_DEBUG_CAPS "enable OpenVINO debug capabilities at runtime" OFF) ov_dependent_option (ENABLE_NPU_DEBUG_CAPS "enable NPU debug capabilities at runtime" ON "ENABLE_DEBUG_CAPS;ENABLE_INTEL_NPU" OFF) diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake new file mode 100644 index 00000000000000..e33f01125f756c --- /dev/null +++ b/cmake/graph-compiler.cmake @@ -0,0 +1,31 @@ +get_property(GRAPH_COMPILER_LIBS GLOBAL PROPERTY GRAPH_COMPILER_LIBS) +if (NOT DEFINED GRAPH_COMPILER_LIBS) + include(FetchContent) + + #FIXME: Replace the repository URL with the https://github.com/intel/graph-compiler + FetchContent_Declare( + GC + GIT_REPOSITORY https://github.com/AndreyPavlenko/graph-compiler.git + GIT_TAG pkg + FIND_PACKAGE_ARGS NAMES GraphCompiler + ) + + set(GC_ENABLE_OPT OFF) + set(GC_ENABLE_TEST OFF) + set(GC_ENABLE_DNNL OFF) + set(GC_ENABLE_LEGACY OFF) + set(GC_ENABLE_BINDINGS_PYTHON OFF) + set(OV_BUILD_SHARED_LIBS_TMP ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF) + FetchContent_MakeAvailable(GC) + set(BUILD_SHARED_LIBS ${OV_BUILD_SHARED_LIBS_TMP}) + + set(GRAPH_COMPILER_LIBS + GcInterface + GcJitWrapper + GcCpuRuntime + ) + set_property(GLOBAL PROPERTY GRAPH_COMPILER_LIBS ${GRAPH_COMPILER_LIBS}) +endif () + +get_target_property(GRAPH_COMPILER_INCLUDES GcInterface INTERFACE_INCLUDE_DIRECTORIES) diff --git a/src/cmake/openvino.cmake b/src/cmake/openvino.cmake index 348087d5d3c320..43a64b71e4f9bd 100644 --- a/src/cmake/openvino.cmake +++ b/src/cmake/openvino.cmake @@ -58,6 +58,7 @@ target_link_libraries(${TARGET_NAME} openvino::shape_inference openvino::pugixml ${CMAKE_DL_LIBS} + ${GRAPH_COMPILER_LIBS} ${MLIR_OPENVINO_LIBS} Threads::Threads PUBLIC $<$,$,9.1>>:stdc++fs> diff --git a/src/common/transformations/CMakeLists.txt b/src/common/transformations/CMakeLists.txt index 9eff9864663570..3c9bcc05c6978c 100644 --- a/src/common/transformations/CMakeLists.txt +++ b/src/common/transformations/CMakeLists.txt @@ -28,12 +28,17 @@ ov_build_target_faster(${TARGET_NAME}_obj target_compile_features(${TARGET_NAME}_obj PUBLIC cxx_std_17) -target_link_libraries(${TARGET_NAME}_obj PRIVATE openvino::reference openvino::itt openvino::core::dev openvino::shape_inference) +target_link_libraries(${TARGET_NAME}_obj PRIVATE + openvino::reference + openvino::itt + openvino::core::dev + openvino::shape_inference) target_include_directories(${TARGET_NAME}_obj PRIVATE "${PUBLIC_HEADERS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/src" "${MLIR_INCLUDE_DIRS}" - "${LLVM_INCLUDE_DIRS}") + "${LLVM_INCLUDE_DIRS}" + "${GRAPH_COMPILER_INCLUDES}") add_tpp_mlir_includes(${TARGET_NAME}_obj) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index da777e30094da1..dbe40c3f0cff6a 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -59,6 +59,10 @@ #include "mlir/Target/LLVMIR/Export.h" #include "mlir/Target/LLVMIR/ModuleTranslation.h" +#ifdef GRAPH_COMPILER +#include "gc/ExecutionEngine/Driver/Driver.h" +#endif + #ifdef TPP_MLIR // If TPP is available #include "TPP/Dialect/Check/CheckDialect.h" #include "TPP/Dialect/Perf/PerfDialect.h" @@ -188,7 +192,7 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, // This pass converts a group of nodes into a single MLIROp -NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph, bool tpp_mlir_enabled) { +NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph, MlirMode mode) { mlir::OwningOpRef module = ngraph_to_mlir(context, subgraph->inputs, subgraph->nodes, subgraph->outputs); const auto& inputs = subgraph->inputs; @@ -232,7 +236,7 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph, bool tpp_m } return std::make_shared( subgraph->inputs, - std::make_shared(std::move(module), tpp_mlir_enabled), + std::make_shared(std::move(module), mode), output_types, output_map ); @@ -253,18 +257,18 @@ void replace_subgraph(SubgraphPtr subgraph, NodePtr node) { class Partitioner : public ov::pass::ModelPass { MLIRContext* context; - bool tpp_mlir_enabled; + MlirMode mode; public: OPENVINO_RTTI("Partitioner"); - Partitioner(MLIRContext* context, bool tpp_mlir_enabled) : + Partitioner(MLIRContext* context, MlirMode mode) : context(context), - tpp_mlir_enabled(tpp_mlir_enabled) + mode(mode) {} bool run_on_model(const std::shared_ptr& model) override { SubgraphTracker tracker([this](SubgraphPtr subgraph) { - auto mlir_op = ngraph_to_mlir_op(context, subgraph, tpp_mlir_enabled); + auto mlir_op = ngraph_to_mlir_op(context, subgraph, mode); replace_subgraph(subgraph, mlir_op); OPENVINO_MLIR_DEBUG_PRINT("Created MLIR op: " << mlir_op << "\n"); } @@ -278,7 +282,7 @@ class Partitioner : public ov::pass::ModelPass { }; -void injectMLIR(std::shared_ptr model, MLIRContext* context, bool tpp_mlir_enabled) { +void injectMLIR(std::shared_ptr model, MLIRContext* context, MlirMode mode) { ov::pass::Manager manager; using namespace ov::op; manager.set_per_pass_validation(false); @@ -289,55 +293,68 @@ void injectMLIR(std::shared_ptr model, MLIRContext* context, bool tpp manager.register_pass>(); manager.register_pass(); manager.register_pass(); - manager.register_pass(context, tpp_mlir_enabled); + manager.register_pass(context, mode); manager.run_passes(model); model->validate_nodes_and_infer_types(); } +void loadDialects(MLIRContext* context) { + context->loadDialect(); + context->loadDialect(); + context->loadDialect(); +} -MLIRContext* get_shared_mlir_context(bool tpp_mlir_enabled_current) { +MLIRContext* get_shared_mlir_context(MlirMode mode) { // Gives MLIRContext instance shared for entire OV process and initialized once upon the initial request // FIXME: Bind with OpenVINO lifetime in the sutable class instead of dirty tricking with static lifetime static std::shared_ptr context; - static bool tpp_mlir_enabled = tpp_mlir_enabled_current; + static bool current_mode = mode; - if(context) { - if(tpp_mlir_enabled_current != tpp_mlir_enabled) { - OPENVINO_MLIR_DEBUG_PRINT("[ DEBUG ] Switched TPP mode, reinitialize MLIR context\n"); - tpp_mlir_enabled = tpp_mlir_enabled_current; - context.reset(); + if (context) { + if (current_mode != mode) { + OPENVINO_MLIR_DEBUG_PRINT("[ DEBUG ] Switching MLIR mode to: "); + current_mode = mode; + } else { + return context.get(); } + } else { + OPENVINO_MLIR_DEBUG_PRINT("[ DEBUG ] MLIR mode: "); } - if (!context) { - +#ifdef GRAPH_COMPILER + if (mode == MLIR_MODE_GC) { + OPENVINO_MLIR_DEBUG_PRINT("GC\n"); + context = std::make_shared(gc::initCompilerAndGetDialects()); + } else { +#endif // Initialize the LLVM machinery llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); - - OPENVINO_MLIR_DEBUG_PRINT("[ DEBUG ] Using TPP_MLIR: "); - if(tpp_mlir_enabled) { - OPENVINO_MLIR_DEBUG_PRINT("YES\n"); +#ifdef TPP_MLIR + if (mode == MLIR_MODE_TPP) { + OPENVINO_MLIR_DEBUG_PRINT("TPP\n"); // Initialize GPU-related LLVM machinery - #ifdef TPP_MLIR - tpp::initializeGpuTargets(); - #endif + tpp::initializeGpuTargets(); } else { - OPENVINO_MLIR_DEBUG_PRINT("NO\n"); - } +#endif + assert(mode == MLIR_MODE_DEFAULT); + OPENVINO_MLIR_DEBUG_PRINT("DEFAULT\n"); +#ifdef TPP_MLIR + } +#endif // Add the following to include *all* MLIR Core dialects, or selectively // include what you need like above. You only need to register dialects that // will be *parsed* by the tool, not the one generated DialectRegistry registry; - if(tpp_mlir_enabled) { - #ifdef TPP_MLIR - registry.insert(); - registry.insert(); - registry.insert(); - #endif +#ifdef TPP_MLIR + if (mode == MLIR_MODE_TPP) { + registry.insert(); + registry.insert(); + registry.insert(); } +#endif registerAllDialects(registry); registerAllExtensions(registry); @@ -347,11 +364,11 @@ MLIRContext* get_shared_mlir_context(bool tpp_mlir_enabled_current) { context = std::make_shared(registry); - context->loadDialect(); - context->loadDialect(); - context->loadDialect(); +#ifdef GRAPH_COMPILER } +#endif + loadDialects(context.get()); return context.get(); } @@ -359,19 +376,44 @@ MLIRContext* get_shared_mlir_context(bool tpp_mlir_enabled_current) { void ov::pass::transformMLIR(std::shared_ptr model) { if(util::getenv_bool("OV_MLIR", true)) { - bool tpp_mlir_default = - #ifdef TPP_MLIR - true; - #else - false; - #endif - bool tpp_mlir_enabled = util::getenv_bool("OV_MLIR_TPP", tpp_mlir_default); - #ifndef TPP_MLIR - OPENVINO_ASSERT(!tpp_mlir_enabled, + const char *default_mode = +#ifdef TPP_MLIR + "TPP"; +#elif defined(GRAPH_COMPILER) + "GC"; +#else + "DEFAULT"; +#endif + auto mode_str = util::getenv_string("OV_MLIR_MODE"); + + if (mode_str == "") { + mode_str = default_mode; + } else { + // Convert to uppercase + std::transform(mode_str.begin(), mode_str.end(), mode_str.begin(), ::toupper); + } + + MlirMode mode; + + if (mode_str == "TPP") { +#ifndef TPP_MLIR + OPENVINO_THROW( "[ ERROR ] OpenVINO wasn't compiled with TPP_MLIR support, " - "but OV_MLIR_TPP environment variable is set to enable it."); - #endif + "but OV_MLIR_MODE environment variable is set to TPP."); +#endif + mode = MLIR_MODE_TPP; + } else if (mode_str == "GC") { +#ifndef GRAPH_COMPILER + OPENVINO_THROW( + "[ ERROR ] OpenVINO wasn't compiled with GRAPH_COMPILER support, " + "but OV_MLIR_MODE environment variable is set to GC."); +#endif + mode = MLIR_MODE_GC; + } else { + OPENVINO_ASSERT(mode_str == "DEFAULT"); + mode = MLIR_MODE_DEFAULT; + } - injectMLIR(model, get_shared_mlir_context(tpp_mlir_enabled), tpp_mlir_enabled); + injectMLIR(model, get_shared_mlir_context(mode), mode); } } diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 74dee5e4db8393..8484f502f69c3a 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -58,6 +58,10 @@ #include "TPP/Passes.h" #endif +#ifdef GRAPH_COMPILER +#include "gc/Transforms/Passes.h" +#endif + namespace { using namespace mlir; @@ -65,72 +69,81 @@ using namespace mlir; using NodePtr = std::shared_ptr; using SymbolPtr = std::shared_ptr; -void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module, bool tpp_mlir_enabled) { +void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module, ov::mlir::MlirMode mode) { PassManager pm(module->getContext()); - if(tpp_mlir_enabled) { - #ifdef TPP_MLIR + + switch (mode) { +#ifdef TPP_MLIR + case ov::mlir::MLIR_MODE_TPP: tpp::DefaultPipelineOptions defPipelineOpts; pm.addPass(tpp::createDefaultPipeline(defPipelineOpts)); - #endif - } else { - // Cleanup before bufferization. - // Simplifies IR to allow better bufferization. - pm.addNestedPass(createCanonicalizerPass()); - pm.addNestedPass(createCSEPass()); - - // Remove empty tensors to avoid converting them into temporary buffers. - pm.addPass(bufferization::createEmptyTensorEliminationPass()); - - pm.addPass(bufferization::createOneShotBufferizePass()); - pm.addNestedPass(bufferization::createFinalizingBufferizePass()); - - // Cleanup after bufferization - possibly remove redundant copies. - pm.addNestedPass(createCanonicalizerPass()); - pm.addNestedPass(createCSEPass()); - - // Deallocation pipeline to avoid memory leaks from created temporary buffers. - pm.addPass(memref::createExpandReallocPass(/*emitDeallocs=*/false)); - pm.addPass(createCanonicalizerPass()); - bufferization::DeallocationOptions deallocOpts; - deallocOpts.privateFuncDynamicOwnership = false; - pm.addPass(bufferization::createOwnershipBasedBufferDeallocationPass(deallocOpts)); - pm.addPass(createCanonicalizerPass()); - pm.addPass(bufferization::createBufferDeallocationSimplificationPass()); - pm.addPass(bufferization::createLowerDeallocationsPass()); - pm.addPass(createCSEPass()); - pm.addPass(createCanonicalizerPass()); - - // Blanket-convert any remaining high-level vector ops to loops if any remain. - pm.addNestedPass(createConvertVectorToSCFPass()); - // pm.addNestedPass(createLinalgGeneralizeNamedOpsPass()); - // Blanket-convert any remaining linalg ops to loops if any remain. - pm.addNestedPass(createConvertLinalgToLoopsPass()); - // Blanket-convert any remaining affine ops if any remain. - pm.addPass(createLowerAffinePass()); - // Convert SCF to CF (always needed). - pm.addPass(createConvertSCFToCFPass()); - // Sprinkle some cleanups. - pm.addPass(createCanonicalizerPass()); - pm.addPass(createCSEPass()); - // Blanket-convert any remaining linalg ops to LLVM if any remain. - // pm.addPass(createConvertLinalgToLLVMPass()); // no such pass - // Convert vector to LLVM (always needed). - pm.addPass(createConvertVectorToLLVMPass()); - // Convert Math to LLVM (always needed). - pm.addNestedPass(createConvertMathToLLVMPass()); - // Expand complicated MemRef operations before lowering them. - pm.addPass(memref::createExpandStridedMetadataPass()); - // The expansion may create affine expressions. Get rid of them. - pm.addPass(createLowerAffinePass()); - // Convert MemRef to LLVM (always needed). - // pm.addPass(memref::createExpandOpsPass()); - pm.addPass(createFinalizeMemRefToLLVMConversionPass()); - // Convert Func to LLVM (always needed). - pm.addPass(createConvertFuncToLLVMPass()); - // Convert Index to LLVM (always needed). - pm.addPass(createConvertIndexToLLVMPass()); - // Convert remaining unrealized_casts (always needed). - pm.addPass(createReconcileUnrealizedCastsPass()); + break; +#endif +#ifdef GRAPH_COMPILER + case ov::mlir::MLIR_MODE_GC: + gc::populateCPUPipeline(pm); + break; +#endif + default: + assert(ov::mlir::MLIR_MODE_DEFAULT); + // Cleanup before bufferization. + // Simplifies IR to allow better bufferization. + pm.addNestedPass(createCanonicalizerPass()); + pm.addNestedPass(createCSEPass()); + + // Remove empty tensors to avoid converting them into temporary buffers. + pm.addPass(bufferization::createEmptyTensorEliminationPass()); + + pm.addPass(bufferization::createOneShotBufferizePass()); + pm.addNestedPass(bufferization::createFinalizingBufferizePass()); + + // Cleanup after bufferization - possibly remove redundant copies. + pm.addNestedPass(createCanonicalizerPass()); + pm.addNestedPass(createCSEPass()); + + // Deallocation pipeline to avoid memory leaks from created temporary buffers. + pm.addPass(memref::createExpandReallocPass(/*emitDeallocs=*/false)); + pm.addPass(createCanonicalizerPass()); + bufferization::DeallocationOptions deallocOpts; + deallocOpts.privateFuncDynamicOwnership = false; + pm.addPass(bufferization::createOwnershipBasedBufferDeallocationPass(deallocOpts)); + pm.addPass(createCanonicalizerPass()); + pm.addPass(bufferization::createBufferDeallocationSimplificationPass()); + pm.addPass(bufferization::createLowerDeallocationsPass()); + pm.addPass(createCSEPass()); + pm.addPass(createCanonicalizerPass()); + + // Blanket-convert any remaining high-level vector ops to loops if any remain. + pm.addNestedPass(createConvertVectorToSCFPass()); + // pm.addNestedPass(createLinalgGeneralizeNamedOpsPass()); + // Blanket-convert any remaining linalg ops to loops if any remain. + pm.addNestedPass(createConvertLinalgToLoopsPass()); + // Blanket-convert any remaining affine ops if any remain. + pm.addPass(createLowerAffinePass()); + // Convert SCF to CF (always needed). + pm.addPass(createConvertSCFToCFPass()); + // Sprinkle some cleanups. + pm.addPass(createCanonicalizerPass()); + pm.addPass(createCSEPass()); + // Blanket-convert any remaining linalg ops to LLVM if any remain. + // pm.addPass(createConvertLinalgToLLVMPass()); // no such pass + // Convert vector to LLVM (always needed). + pm.addPass(createConvertVectorToLLVMPass()); + // Convert Math to LLVM (always needed). + pm.addNestedPass(createConvertMathToLLVMPass()); + // Expand complicated MemRef operations before lowering them. + pm.addPass(memref::createExpandStridedMetadataPass()); + // The expansion may create affine expressions. Get rid of them. + pm.addPass(createLowerAffinePass()); + // Convert MemRef to LLVM (always needed). + // pm.addPass(memref::createExpandOpsPass()); + pm.addPass(createFinalizeMemRefToLLVMConversionPass()); + // Convert Func to LLVM (always needed). + pm.addPass(createConvertFuncToLLVMPass()); + // Convert Index to LLVM (always needed). + pm.addPass(createConvertIndexToLLVMPass()); + // Convert remaining unrealized_casts (always needed). + pm.addPass(createReconcileUnrealizedCastsPass()); } auto result = pm.run(module.get()); @@ -206,10 +219,10 @@ std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext } // TODO: u4/i4 types are not supported -struct MemRef { - MemRef() = default; +struct MemRefDescriptor { + MemRefDescriptor() = default; - MemRef(ov::Tensor tensor) + MemRefDescriptor (ov::Tensor tensor) : allocated(tensor.data()), aligned(tensor.data()), offset(0), @@ -252,7 +265,7 @@ namespace mlir { using namespace ::mlir; -MLIREvaluate::MLIREvaluate(OwningOpRef _module, bool tpp_mlir_enabled) : +MLIREvaluate::MLIREvaluate(OwningOpRef _module, MlirMode mode) : module(std::move(_module)) { OPENVINO_MLIR_DEBUG_PRINT( @@ -262,7 +275,7 @@ MLIREvaluate::MLIREvaluate(OwningOpRef _module, bool tpp_mlir_en OPENVINO_MLIR_DEBUG_PRINT( "-----------------------------------------\n"); - prepareMLIRKernelWithoutWrapper(module, tpp_mlir_enabled); + prepareMLIRKernelWithoutWrapper(module, mode); OPENVINO_MLIR_DEBUG_PRINT( "[ DEBUG ] Target LLVM:\n" @@ -317,9 +330,9 @@ NodePtr MLIROp::clone_with_new_inputs(const ov::OutputVector& new_args) const { } bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const { - std::vector memref_args; + std::vector memref_args; for (size_t i = 0; i < inputs.size(); ++i) { - memref_args.push_back(MemRef(inputs[i])); + memref_args.push_back(MemRefDescriptor(inputs[i])); } for (size_t i = 0; i < outputs.size(); ++i) { // TODO: Optimize by adding all dimensions to dimensions_map, not only dynamic @@ -337,11 +350,11 @@ bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) } //std::cerr << "[ DEBUG ] Set outputs[" << i << "].shape(" << target << ")\n"; outputs[i].set_shape(target); - memref_args.push_back(MemRef(outputs[i])); + memref_args.push_back(MemRefDescriptor(outputs[i])); } std::vector args; - std::for_each(memref_args.begin(), memref_args.end(), [&args](MemRef& x) { + std::for_each(memref_args.begin(), memref_args.end(), [&args](MemRefDescriptor& x) { x.append_to_packed_args(args); }); diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.hpp b/src/common/transformations/src/transformations/mlir/mlir_op.hpp index 72fbcc462cd805..a0ffc58713c30e 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.hpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.hpp @@ -23,13 +23,20 @@ using ::mlir::ModuleOp; using ::mlir::ExecutionEngine; using ::mlir::ModuleOp; +enum MlirMode { + MLIR_MODE_TPP, + MLIR_MODE_GC, + MLIR_MODE_DEFAULT, +}; + + class MLIREvaluate { OwningOpRef module; // FIXME: needs to be kept? std::unique_ptr engine; public: - MLIREvaluate(OwningOpRef _module, bool tpp_mlir_enabled); + MLIREvaluate(OwningOpRef _module, MlirMode mode); bool invoke_packed(std::vector& args); }; From ee1d2b474428e5644d85d2662eb949f83cb64190 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Mon, 5 Aug 2024 14:03:43 +0200 Subject: [PATCH 029/121] [GraphCompiler] Use find_package() for CMake < 3.24 (#162) * [GraphCompiler] Use find_package() for CMake < 3.24 * Changed the Graph Compiler git url --- cmake/graph-compiler.cmake | 41 ++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index e33f01125f756c..30b9e5b6913d14 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -1,24 +1,31 @@ get_property(GRAPH_COMPILER_LIBS GLOBAL PROPERTY GRAPH_COMPILER_LIBS) if (NOT DEFINED GRAPH_COMPILER_LIBS) - include(FetchContent) + # The FetchContent_Declare(FIND_PACKAGE_ARGS) is supported since CMake 3.24. For the prior + # versions, using find_package() first. If the package is not found, then using FetchContent. + if (CMAKE_VERSION VERSION_LESS "3.24") + find_package(GraphCompiler QUIET) + endif () - #FIXME: Replace the repository URL with the https://github.com/intel/graph-compiler - FetchContent_Declare( - GC - GIT_REPOSITORY https://github.com/AndreyPavlenko/graph-compiler.git - GIT_TAG pkg - FIND_PACKAGE_ARGS NAMES GraphCompiler - ) + if (NOT GraphCompiler_FOUND) + include(FetchContent) + + FetchContent_Declare( + GC + GIT_REPOSITORY https://github.com/intel/graph-compiler.git + GIT_TAG main + FIND_PACKAGE_ARGS NAMES GraphCompiler + ) - set(GC_ENABLE_OPT OFF) - set(GC_ENABLE_TEST OFF) - set(GC_ENABLE_DNNL OFF) - set(GC_ENABLE_LEGACY OFF) - set(GC_ENABLE_BINDINGS_PYTHON OFF) - set(OV_BUILD_SHARED_LIBS_TMP ${BUILD_SHARED_LIBS}) - set(BUILD_SHARED_LIBS OFF) - FetchContent_MakeAvailable(GC) - set(BUILD_SHARED_LIBS ${OV_BUILD_SHARED_LIBS_TMP}) + set(GC_ENABLE_OPT OFF) + set(GC_ENABLE_TEST OFF) + set(GC_ENABLE_DNNL OFF) + set(GC_ENABLE_LEGACY OFF) + set(GC_ENABLE_BINDINGS_PYTHON OFF) + set(OV_BUILD_SHARED_LIBS_TMP ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF) + FetchContent_MakeAvailable(GC) + set(BUILD_SHARED_LIBS ${OV_BUILD_SHARED_LIBS_TMP}) + endif () set(GRAPH_COMPILER_LIBS GcInterface From 21c61af3cd3913c631d7f5876abf297cd6faeaf1 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Mon, 5 Aug 2024 14:04:13 +0200 Subject: [PATCH 030/121] Fixed build failure caused by #155 (#163) --- .../transformations/src/transformations/mlir/mlir_op.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 8484f502f69c3a..afe777ef949e6f 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -74,17 +74,19 @@ void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module, switch (mode) { #ifdef TPP_MLIR - case ov::mlir::MLIR_MODE_TPP: + case ov::mlir::MLIR_MODE_TPP: { tpp::DefaultPipelineOptions defPipelineOpts; pm.addPass(tpp::createDefaultPipeline(defPipelineOpts)); break; + } #endif #ifdef GRAPH_COMPILER - case ov::mlir::MLIR_MODE_GC: + case ov::mlir::MLIR_MODE_GC: { gc::populateCPUPipeline(pm); break; + } #endif - default: + default: { assert(ov::mlir::MLIR_MODE_DEFAULT); // Cleanup before bufferization. // Simplifies IR to allow better bufferization. @@ -144,6 +146,7 @@ void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module, pm.addPass(createConvertIndexToLLVMPass()); // Convert remaining unrealized_casts (always needed). pm.addPass(createReconcileUnrealizedCastsPass()); + } } auto result = pm.run(module.get()); From 966634ebf840f48db7f664771cae391608f9ead7 Mon Sep 17 00:00:00 2001 From: Renato Golin Date: Mon, 5 Aug 2024 19:42:48 +0100 Subject: [PATCH 031/121] multi-layer support in benchmark scripts (#164) Allows to build OV benchmarks with multiple layers, both baseline and pytorch. --------- Co-authored-by: Adam Siemieniuk --- tools/mlir_bench/mlp_bench.sh | 25 ++++++++++++++++++++----- tools/mlir_bench/ov_model_gen.py | 23 +++++++++++++++++------ 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/tools/mlir_bench/mlp_bench.sh b/tools/mlir_bench/mlp_bench.sh index b9253549f42eef..76da5276479071 100755 --- a/tools/mlir_bench/mlp_bench.sh +++ b/tools/mlir_bench/mlp_bench.sh @@ -10,12 +10,13 @@ die_syntax() { echo "" echo " -t: Optional data type" echo " -b: Optional baseline model" + echo " -l: Optional number of layers (def:3)" echo " -D: Set model shapes to dynamic" exit 1 } # Cmd-line opts -while getopts "t:b:D" arg; do +while getopts "t:l:b:D" arg; do case ${arg} in t) DATA_TYPE=${OPTARG} @@ -23,6 +24,9 @@ while getopts "t:b:D" arg; do b) BASELINE_MODEL=${OPTARG} ;; + l) + NUM_LAYERS=${OPTARG} + ;; D) IS_DYNAMIC=true ;; @@ -33,6 +37,10 @@ while getopts "t:b:D" arg; do esac done +if [ ! $NUM_LAYERS ]; then + NUM_LAYERS=3 +fi + OV_ROOT=$(git rev-parse --show-toplevel) BENCH_ROOT=$(realpath "${OV_ROOT}/tools/mlir_bench") @@ -62,8 +70,10 @@ if [ "${BASELINE_MODEL}" ] && [ "${IS_DYNAMIC}" ]; then fi # Kernel config. -LAYERS=( 1024 2048 4096 8192 ) -MINI_BATCHES=( 128 256 512 ) +#LAYERS=( 1024 2048 4096 8192 ) +#MINI_BATCHES=( 128 256 512 ) +LAYERS=( 1024 ) +MINI_BATCHES=( 256 ) if [ ! "${DATA_TYPE}" ]; then DATA_TYPE="f32" fi @@ -76,11 +86,16 @@ for MB in "${MINI_BATCHES[@]}"; do # Generate model. if [ "${BASELINE_MODEL}" ]; then # Enable baseline model flag. - MODEL_CONFIG=(-b="${BASELINE_MODEL}[${MB},${LAYER},${LAYER}]") + MODEL_CONFIG=(-b="${BASELINE_MODEL}[${MB},${LAYER},${LAYER}]x${NUM_LAYERS}") else # Generate default PyTorch MLP. - MODEL_CONFIG=(-l="linear[${MB},${LAYER},${LAYER}] relu[]") + LAYER_STRING="linear[${MB},${LAYER},${LAYER}] relu[]" + for i in $(seq ${NUM_LAYERS}); do + MODEL_STRING="${MODEL_STRING}${LAYER_STRING} " + done + MODEL_CONFIG=(-l="${MODEL_STRING}") fi + echo "MODEL_CONFIG=${MODEL_CONFIG}" GEN_FLAGS=(-t ${DATA_TYPE} -n ${MODEL_NAME}) if [ "${IS_DYNAMIC}" ]; then GEN_FLAGS+=(--dynamic) diff --git a/tools/mlir_bench/ov_model_gen.py b/tools/mlir_bench/ov_model_gen.py index 56e0e82ce81618..f233bac14edd2d 100644 --- a/tools/mlir_bench/ov_model_gen.py +++ b/tools/mlir_bench/ov_model_gen.py @@ -115,6 +115,11 @@ def get_layer_sizes(layer_desc: str) -> list[int]: return [int(size) for size in filter(None, desc_sizes.split(','))] +def get_layer_num_layers(layer_desc: str) -> int: + layers = layer_desc[layer_desc.find('x')+1:] + return int(layers) + + def parse_layer(layer_desc: str, type: str) -> nn.Module: layer = get_layer_name(layer_desc) sizes = get_layer_sizes(layer_desc) @@ -169,22 +174,28 @@ def generate_ov_model(layers_desc: str, data_type: str, file_name: str, class BaselineMLP(nn.Module): - def __init__(self, sizes_mnk, type=None): + def __init__(self, sizes_mnk, type=None, layers=3): super(BaselineMLP, self).__init__() m = sizes_mnk[0] n = sizes_mnk[1] self.bias = torch.empty((m, n), dtype=type).data.fill_(0.01) self.relu = nn.ReLU() + self.layers = layers def forward(self, a, b): - c = torch.matmul(a, b) - c = torch.add(c, self.bias) - return self.relu(c) + for _ in range(0,self.layers): + c = torch.matmul(a, b) + c = torch.add(c, self.bias) + a = self.relu(c) + return a def baseline_MLP(model_desc: str, data_type: str, is_dynamic: bool) -> tuple[nn.Model, list]: sizes = get_layer_sizes(model_desc) assert len(sizes) == 3, "Invalid baseline MLP sizes" - mlp = BaselineMLP(sizes, get_torch_type(data_type)) + layers = get_layer_num_layers(model_desc) + if (layers is None): + layers = 3 # Default to 3 layers + mlp = BaselineMLP(sizes, get_torch_type(data_type), layers) input_shapes = get_layer_inputs(model_desc, is_dynamic) m = input_shapes[0] n = input_shapes[1] @@ -224,7 +235,7 @@ def main(): help='Name for exported XML model') parser.add_argument('-b', '--baseline', default=None, type=str.lower, help='Baseline pre-made model - overrides layers. For example:\ - -b=mlp[32,64,16]') + -b=mlp[32,64,16]x10') parser.add_argument('-p', '--print', action='store_true', help='Compile and print the model') args = parser.parse_args() From acedae259723b4995049e84128e3c15b581d02fc Mon Sep 17 00:00:00 2001 From: Rolf Morel Date: Thu, 8 Aug 2024 02:50:34 -0700 Subject: [PATCH 032/121] [MLIR][DLTI] Add DLTI attr to MLIR-generated modules This change serves as a PoC of OV being able to communicate hints to the MLIR-compiler that is responible for the subgraph. As a PoC we just pass the magic number 32 as a tile size hint. Later changes can incorporate OV-based logic for deriving the values for these hints. --- .../src/transformations/mlir/convert.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index dbe40c3f0cff6a..60b86b263bfa14 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -31,6 +31,7 @@ #include "mlir/Dialect/Arith/IR/Arith.h" #include "mlir/Dialect/Arith/Transforms/Passes.h" #include "mlir/Dialect/Bufferization/Transforms/Passes.h" +#include "mlir/Dialect/DLTI/DLTI.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/Linalg/Passes.h" @@ -133,6 +134,16 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, auto func = moduleBuilder.create(funcLoc, "entry", funcType); auto block_builder = mlir::OpBuilder::atBlockBegin(func.addEntryBlock() /* TODO: Add logger here */); + // Affix target information attribute to the module to be used, at its discretion, + // by the MLIR-compiler that consumes this module. + auto tileSize = IntegerAttr::get(IntegerType::get(context, 32), 32); + auto key = StringAttr::get(context, "tile_size"); + DataLayoutEntryInterface entry = DataLayoutEntryAttr::get(context, key, tileSize); + TargetDeviceSpecInterface deviceSpec = TargetDeviceSpecAttr::get(context, ArrayRef(entry)); + auto deviceStr = StringAttr::get(context, "CPU"); + auto sysSpec = TargetSystemSpecAttr::get(context, ArrayRef(std::pair(deviceStr, deviceSpec))); + module.getOperation()->setAttr("#dlti.sys_spec", sysSpec); + ConversionContext conversion_context(context, &block_builder); for (size_t i = 0; i < inputs.size(); ++i) { @@ -299,6 +310,7 @@ void injectMLIR(std::shared_ptr model, MLIRContext* context, MlirMode } void loadDialects(MLIRContext* context) { + context->loadDialect(); context->loadDialect(); context->loadDialect(); context->loadDialect(); From 09888608b8a21d465df72cffc2861777af46481b Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Fri, 9 Aug 2024 20:36:33 +0200 Subject: [PATCH 033/121] Unify bench configs (#166) Adjusts all MLIR benchmarks to follow recent mlp_bench changes. Adds and propagates control for number of MLP layers to be measured. Sets environment variable to enable MLIR IR dump from OV. --- tools/mlir_bench/libxsmm_bench.sh | 25 +++++++++++---- tools/mlir_bench/mlp_bench.sh | 4 +-- tools/mlir_bench/ov_raw_mlir_bench.sh | 30 +++++++++++++----- tools/mlir_bench/run_bench_bf16.sh | 45 +++++++++++++++++++++------ tools/mlir_bench/run_bench_f32.sh | 45 +++++++++++++++++++++------ tools/mlir_bench/tpp_mlir_bench.sh | 25 +++++++++++---- 6 files changed, 134 insertions(+), 40 deletions(-) diff --git a/tools/mlir_bench/libxsmm_bench.sh b/tools/mlir_bench/libxsmm_bench.sh index c4d43a545e3ec5..191a87e48a74e9 100755 --- a/tools/mlir_bench/libxsmm_bench.sh +++ b/tools/mlir_bench/libxsmm_bench.sh @@ -6,15 +6,16 @@ # Runs MLP benchmarks using libxsmm. die_syntax() { - echo "Syntax: $0 [-B] [-D]" + echo "Syntax: $0 [-B] [-D] [-l 3]" echo "" echo " -B: Use bf16 data type" + echo " -l: Optional number of layers (def:3)" echo " -D: Set model shapes to dynamic" exit 1 } # Cmd-line opts -while getopts "BD" arg; do +while getopts "l:BD" arg; do case ${arg} in B) DATA_TYPE="bf16" @@ -22,6 +23,9 @@ while getopts "BD" arg; do D) IS_DYNAMIC=true ;; + l) + NUM_LAYERS=${OPTARG} + ;; ?) echo "Invalid option: ${OPTARG}" die_syntax @@ -42,13 +46,18 @@ if [ "${IS_DYNAMIC}" ]; then fi # Kernel config. -LAYERS=( 1024 2048 4096 8192 ) -MINI_BATCHES=( 128 256 512 ) +#LAYERS=( 1024 2048 4096 8192 ) +#MINI_BATCHES=( 128 256 512 ) +LAYERS=( 1024 ) +MINI_BATCHES=( 256 ) if [ ! "${DATA_TYPE}" ]; then DATA_TYPE="f32" fi +if [ ! $NUM_LAYERS ]; then + NUM_LAYERS=3 +fi -echo "Result type: GFLOPS" +echo "Result type: GFLOPS - NUM LAYERS: ${NUM_LAYERS}" for MB in "${MINI_BATCHES[@]}"; do echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" for LAYER in "${LAYERS[@]}"; do @@ -61,10 +70,14 @@ for MB in "${MINI_BATCHES[@]}"; do if [ "${DATA_TYPE}" = "bf16" ]; then LAYOUT=(1 1) fi + LAYER_STRING="${LAYER}" + for i in $(seq ${NUM_LAYERS}); do + LAYER_STRING="${LAYER_STRING} ${LAYER}" + done # Disable parallelism. ENV_FLAGS=OMP_NUM_THREADS=1 exec env ${ENV_FLAGS} ${BENCH_RUNNER} ${NUM_ITER} ${MB} ${FUSE_TYPE} ${TYPE} ${TILES[@]} \ - ${LAYOUT[@]} ${LAYER} ${LAYER} \ + ${LAYOUT[@]} ${LAYER_STRING} \ | sed -nE "s/.*GFLOPS\s+=\s*([0-9.]+).*/\\1/p" done done diff --git a/tools/mlir_bench/mlp_bench.sh b/tools/mlir_bench/mlp_bench.sh index 76da5276479071..2317607d20c662 100755 --- a/tools/mlir_bench/mlp_bench.sh +++ b/tools/mlir_bench/mlp_bench.sh @@ -6,7 +6,7 @@ # Runs OV MLP benchmarks. die_syntax() { - echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-b (mlp)] [-D]" + echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-b (mlp)] [-D] [-l 3]" echo "" echo " -t: Optional data type" echo " -b: Optional baseline model" @@ -79,7 +79,7 @@ if [ ! "${DATA_TYPE}" ]; then fi MODEL_NAME="MLIR_MLP_BENCH.xml" -echo "Result type: time [ms]" +echo "Result type: time [ms] - NUM LAYERS: ${NUM_LAYERS}" for MB in "${MINI_BATCHES[@]}"; do echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" for LAYER in "${LAYERS[@]}"; do diff --git a/tools/mlir_bench/ov_raw_mlir_bench.sh b/tools/mlir_bench/ov_raw_mlir_bench.sh index 2d6a786f349410..dd11b91ff810c3 100755 --- a/tools/mlir_bench/ov_raw_mlir_bench.sh +++ b/tools/mlir_bench/ov_raw_mlir_bench.sh @@ -8,16 +8,17 @@ # For example, the whole graph is outlined to MLIR. die_syntax() { - echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-b (mlp)] [-D]" + echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-b (mlp)] [-D] [-l 3]" echo "" echo " -t: Optional data type" echo " -b: Optional baseline model" + echo " -l: Optional number of layers (def:3)" echo " -D: Set model shapes to dynamic" exit 1 } # Cmd-line opts -while getopts "t:b:D" arg; do +while getopts "t:b:l:D" arg; do case ${arg} in t) DATA_TYPE=${OPTARG} @@ -25,6 +26,9 @@ while getopts "t:b:D" arg; do b) BASELINE_MODEL=${OPTARG} ;; + l) + NUM_LAYERS=${OPTARG} + ;; D) IS_DYNAMIC=true ;; @@ -64,28 +68,38 @@ if [ "${IS_DYNAMIC}" ]; then fi # Kernel config. -LAYERS=( 1024 2048 4096 8192 ) -MINI_BATCHES=( 128 256 512 ) +# LAYERS=( 1024 2048 4096 8192 ) +# MINI_BATCHES=( 128 256 512 ) +LAYERS=( 1024 ) +MINI_BATCHES=( 256 ) if [ ! "${DATA_TYPE}" ]; then DATA_TYPE="f32" fi +if [ ! $NUM_LAYERS ]; then + NUM_LAYERS=3 +fi MODEL_NAME="TPP_BENCH.xml" -echo "Result type: time [s]" +echo "Result type: time [s] - NUM LAYERS: ${NUM_LAYERS}" for MB in "${MINI_BATCHES[@]}"; do echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" for LAYER in "${LAYERS[@]}"; do # Generate model. if [ "${BASELINE_MODEL}" ]; then # Enable baseline model flag. - MODEL_CONFIG=(-b="${BASELINE_MODEL}[${MB},${LAYER},${LAYER}]") + MODEL_CONFIG=(-b="${BASELINE_MODEL}[${MB},${LAYER},${LAYER}]x${NUM_LAYERS}") else # Generate default PyTorch MLP. - MODEL_CONFIG=(-l="linear[${MB},${LAYER},${LAYER}] relu[]") + LAYER_STRING="linear[${MB},${LAYER},${LAYER}] relu[]" + for i in $(seq ${NUM_LAYERS}); do + MODEL_STRING="${MODEL_STRING}${LAYER_STRING} " + done + MODEL_CONFIG=(-l="${MODEL_STRING}") fi + echo "MODEL_CONFIG=${MODEL_CONFIG}" GEN_FLAGS=(-t ${DATA_TYPE} -n ${MODEL_NAME}) GEN_FLAGS+=(-p) - ENV_FLAGS=OV_MLIR_TPP=0 + ENV_FLAGS="OV_MLIR_TPP=0 OV_MLIR_DEBUG=1" MODEL_OUT=$(exec env ${ENV_FLAGS} python3 ${MODEL_GEN} "${MODEL_CONFIG[@]}" "${GEN_FLAGS[@]}" 2>&1) if [ $? != 0 ]; then echo "Failed to generate model" diff --git a/tools/mlir_bench/run_bench_bf16.sh b/tools/mlir_bench/run_bench_bf16.sh index f6d031d29a5222..a03c0f3d35a163 100755 --- a/tools/mlir_bench/run_bench_bf16.sh +++ b/tools/mlir_bench/run_bench_bf16.sh @@ -1,28 +1,55 @@ #!/bin/bash +die_syntax() { + echo "Syntax: $0 [-l 3]" + echo "" + echo " -l: Optional number of layers (def: 3)" + exit 1 +} + +# Cmd-line opts +while getopts "l:" arg; do + case ${arg} in + l) + NUM_LAYERS=${OPTARG} + ;; + ?) + echo "Invalid option: ${OPTARG}" + die_syntax + ;; + esac +done + +if [ ! "${NUM_LAYERS}" ]; then + NUM_LAYERS=3 +fi + +export OV_MLIR_DEBUG=1 + echo "### MLP BF16 benchmarks ###" +echo "# Layers: ${NUM_LAYERS} #" echo "LIBXSMM" -../tools/mlir_bench/libxsmm_bench.sh -B +../tools/mlir_bench/libxsmm_bench.sh -B -l ${NUM_LAYERS} echo "TPP-MLIR args weights" -../tools/mlir_bench/tpp_mlir_bench.sh -t bf16 +../tools/mlir_bench/tpp_mlir_bench.sh -t bf16 -l ${NUM_LAYERS} echo "" echo "Baseline MLP" echo "OV - no MLIR - baseline model" -OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t bf16 -b mlp +OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t bf16 -b mlp -l ${NUM_LAYERS} echo "OV + MLIR - kernel only - baseline model" -../tools/mlir_bench/ov_raw_mlir_bench.sh -t bf16 -b mlp +../tools/mlir_bench/ov_raw_mlir_bench.sh -t bf16 -b mlp -l ${NUM_LAYERS} echo "OV + MLIR - full - baseline model" -OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t bf16 -b mlp +OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t bf16 -b mlp -l ${NUM_LAYERS} echo "" echo "PyTorch MLP" echo "OV - no MLIR - PyTorch" -OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t bf16 +OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t bf16 -l ${NUM_LAYERS} echo "OV + MLIR - kernel only - PyTorch" -../tools/mlir_bench/ov_raw_mlir_bench.sh -t bf16 +../tools/mlir_bench/ov_raw_mlir_bench.sh -t bf16 -l ${NUM_LAYERS} echo "OV + MLIR - full - PyTorch" -OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t bf16 +OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t bf16 -l ${NUM_LAYERS} echo "TPP-MLIR const weights" -../tools/mlir_bench/tpp_mlir_bench.sh -t bf16 -C +../tools/mlir_bench/tpp_mlir_bench.sh -t bf16 -C -l ${NUM_LAYERS} diff --git a/tools/mlir_bench/run_bench_f32.sh b/tools/mlir_bench/run_bench_f32.sh index de7de3fbf50695..19c5125d6138cc 100755 --- a/tools/mlir_bench/run_bench_f32.sh +++ b/tools/mlir_bench/run_bench_f32.sh @@ -1,28 +1,55 @@ #!/bin/bash +die_syntax() { + echo "Syntax: $0 [-l 3]" + echo "" + echo " -l: Optional number of layers (def: 3)" + exit 1 +} + +# Cmd-line opts +while getopts "l:" arg; do + case ${arg} in + l) + NUM_LAYERS=${OPTARG} + ;; + ?) + echo "Invalid option: ${OPTARG}" + die_syntax + ;; + esac +done + +if [ ! "${NUM_LAYERS}" ]; then + NUM_LAYERS=3 +fi + +export OV_MLIR_DEBUG=1 + echo "### MLP F32 benchmarks ###" +echo "# Layers: ${NUM_LAYERS} #" echo "LIBXSMM" -../tools/mlir_bench/libxsmm_bench.sh +../tools/mlir_bench/libxsmm_bench.sh -l ${NUM_LAYERS} echo "TPP-MLIR args weights" -../tools/mlir_bench/tpp_mlir_bench.sh -t f32 +../tools/mlir_bench/tpp_mlir_bench.sh -t f32 -l ${NUM_LAYERS} echo "" echo "Baseline MLP" echo "OV - no MLIR - baseline model" -OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t f32 -b mlp +OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t f32 -b mlp -l ${NUM_LAYERS} echo "OV + MLIR - kernel only - baseline model" -../tools/mlir_bench/ov_raw_mlir_bench.sh -t f32 -b mlp +../tools/mlir_bench/ov_raw_mlir_bench.sh -t f32 -b mlp -l ${NUM_LAYERS} echo "OV + MLIR - full - baseline model" -OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t f32 -b mlp +OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t f32 -b mlp -l ${NUM_LAYERS} echo "" echo "PyTorch MLP" echo "OV - no MLIR - PyTorch" -OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t f32 +OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t f32 -l ${NUM_LAYERS} echo "OV + MLIR - kernel only - PyTorch" -../tools/mlir_bench/ov_raw_mlir_bench.sh -t f32 +../tools/mlir_bench/ov_raw_mlir_bench.sh -t f32 -l ${NUM_LAYERS} echo "OV + MLIR - full - PyTorch" -OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t f32 +OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t f32 -l ${NUM_LAYERS} echo "TPP-MLIR const weights" -../tools/mlir_bench/tpp_mlir_bench.sh -t f32 -C +../tools/mlir_bench/tpp_mlir_bench.sh -t f32 -C -l ${NUM_LAYERS} diff --git a/tools/mlir_bench/tpp_mlir_bench.sh b/tools/mlir_bench/tpp_mlir_bench.sh index 6f07340a08333c..0b2da819ecb478 100755 --- a/tools/mlir_bench/tpp_mlir_bench.sh +++ b/tools/mlir_bench/tpp_mlir_bench.sh @@ -6,20 +6,24 @@ # Runs MLIR only MLP benchmarks using TPP-MLIR. die_syntax() { - echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-D] [-C]" + echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-D] [-C] [-l 3]" echo "" echo " -t: Optional data type" + echo " -l: Optional number of layers (def:3)" echo " -D: Set model shapes to dynamic" echo " -C: Weights as constants (default: arguments)" exit 1 } # Cmd-line opts -while getopts "t:DC" arg; do +while getopts "t:l:DC" arg; do case ${arg} in t) DATA_TYPE=${OPTARG} ;; + l) + NUM_LAYERS=${OPTARG} + ;; D) IS_DYNAMIC=true ;; @@ -51,18 +55,27 @@ if [ "${IS_DYNAMIC}" ]; then fi # Kernel config. -LAYERS=( 1024 2048 4096 8192 ) -MINI_BATCHES=( 128 256 512 ) +# LAYERS=( 1024 2048 4096 8192 ) +# MINI_BATCHES=( 128 256 512 ) +LAYERS=( 1024 ) +MINI_BATCHES=( 256 ) if [ ! "${DATA_TYPE}" ]; then DATA_TYPE="f32" fi +if [ ! $NUM_LAYERS ]; then + NUM_LAYERS=3 +fi -echo "Result type: time [s]" +echo "Result type: time [s] - NUM LAYERS: ${NUM_LAYERS}" for MB in "${MINI_BATCHES[@]}"; do echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" for LAYER in "${LAYERS[@]}"; do # Generate model. - MODEL_CONFIG=(--batch=${MB} --layers=${LAYER},${LAYER} -bias -relu) + LAYER_STRING="${LAYER}" + for i in $(seq ${NUM_LAYERS}); do + LAYER_STRING="${LAYER_STRING},${LAYER}" + done + MODEL_CONFIG=(--batch=${MB} --layers=${LAYER_STRING} -bias -relu) KERNEL_TYPE=args if [ "${CONST_WEIGHTS}" ]; then KERNEL_TYPE=const From da56c3cfab4175a80c7655254ce41fcf175b740e Mon Sep 17 00:00:00 2001 From: Rolf Morel Date: Tue, 3 Sep 2024 02:18:57 -0700 Subject: [PATCH 034/121] Add simple script to benchmark LoRA fragment Requires that Xonsh is installed. --- tools/mlir_bench/lora-runner.xsh | 72 ++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 tools/mlir_bench/lora-runner.xsh diff --git a/tools/mlir_bench/lora-runner.xsh b/tools/mlir_bench/lora-runner.xsh new file mode 100755 index 00000000000000..cc6fe3449bdbc2 --- /dev/null +++ b/tools/mlir_bench/lora-runner.xsh @@ -0,0 +1,72 @@ +#!/usr/bin/env xonsh + +# xonsh can be installed with `pip install xonsh` +# xonsh can then be run by invoking `python -m xonsh` +# this script in particular can be invoked with `python -m xonsh lora-benchmark.xsh` + +import openvino as ov +from openvino.runtime.op import Constant +from openvino_devtools.builder import OpFactory, outputs_to_nodes +import numpy as np +from pprint import pprint +import re + + +SIZES = [8, 16, 32, 64, 128, 256, 512, 1024] +ITERATIONS = 100 + +BENCH_RUNNER="tpp-run" +BENCH_FLAGS=f"-entry-point-result=void -e entry -seed 123 -n {ITERATIONS}".split() + + +def build_lora_model(x_dyn_dim): + opset = OpFactory('opset13') + + #t40 = opset.Parameter({'shape': [-1, -1, 2048], 'element_type': 'f32'}, output_names=[{'x'}]) # Input data + t40 = opset.Parameter({'shape': [x_dyn_dim, 2048], 'element_type': 'f32'}, output_names=[{'x'}]) # Input data + t52 = opset.Parameter({'shape': [1, 8], 'element_type': 'f32'}, output_names=[{'alpha'}]) # LoRA alpha parameter + + t48 = Constant(np.random.rand(2048, 2048).astype(np.float32)) # -> f32[2048,2048] # Original weight matrix W (usually it is compressed to bf16/f16/u8/u4 and represented as a sub-graph) + t50 = Constant(np.random.rand(8, 2048).astype(np.float32)) # -> f32[8,2048] # LoRA matrix A + t54 = Constant(np.random.rand(2048, 8).astype(np.float32)) # -> f32[2048,8] # LoRA matrix B + + t49 = opset.MatMul([t40, t48], {'transpose_a': False, 'transpose_b': True}) # f32[?,?,2048], f32[2048,2048] -> f32[?,?,2048] + t51 = opset.MatMul([t40, t50], {'transpose_a': False, 'transpose_b': True}) # f32[?,?,2048], f32[8,2048] -> f32[?,?,8] + t53 = opset.Multiply([t51, t52], {'auto_broadcast': 'numpy'}) # f32[?,?,8], f32[1,8] -> f32[?,?,8] + t55 = opset.MatMul([t53, t54], {'transpose_a': False, 'transpose_b': True}) # f32[?,?,8], f32[2048,8] -> f32[?,?,2048] + t56 = opset.Add([t49, t55], {'auto_broadcast': 'numpy'}) # f32[?,?,2048], f32[?,?,2048] -> f32[?,?,2048] + t57 = opset.Result([t56], {}) # f32[?,?,2048] -> f32[?,?,2048] + + parameters = [t40, t52] + results = [t57] + sinks = [] + return ov.Model(outputs_to_nodes(results), outputs_to_nodes(sinks), outputs_to_nodes(parameters)) + + +def main(): + no_mlir_averages = [] + mlir_averages = [] + no_ov_averages = [] + for size in SIZES: + model_xml = f"lora.{size}.xml" + model = build_lora_model(size) + ov.save_model(model, model_xml) + + def do_it(env_str): + out = $(env @(env_str) benchmark_app -m @(model_xml) -d CPU -niter @(ITERATIONS) -hint none -nstreams 1 -nthreads 1) + match = re.search(r"Average: +(\d.*) ms", out) + return float(match.group(1)) + no_mlir_averages.append(do_it("OV_MLIR=0")) + mlir_averages.append(do_it("OV_MLIR=1")) + + raw_kernel_secs = $(env OV_MLIR=1 OV_MLIR_TPP=1 OV_MLIR_DEBUG=1 benchmark_app -m @(model_xml) -d CPU -niter 1 -hint none -nstreams 1 -nthreads 1 2>&1 | awk '/Source MLIR:/{flag=1; next} /Target LLVM:/{flag=0} flag' | grep -vE '^[-]+$' | tpp-run @(BENCH_FLAGS)) + no_ov_averages.append(float(raw_kernel_secs) * 1000) + + print("SIZES", SIZES) + print("OV NO-MLIR", no_mlir_averages) + print("OV MLIR", mlir_averages) + print("NO-OV MLIR", no_ov_averages) + + +if __name__ == "__main__": + main() From 34b78176bb0cdfbc77daf2a90adf73e86c912d77 Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Fri, 6 Sep 2024 15:55:38 +0200 Subject: [PATCH 035/121] Control LoRA bench inference precision (#170) Adds extra control flags to keep inference precision the same as the input data type. --- tools/mlir_bench/lora-runner.xsh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/mlir_bench/lora-runner.xsh b/tools/mlir_bench/lora-runner.xsh index cc6fe3449bdbc2..f4bdcfdd69c881 100755 --- a/tools/mlir_bench/lora-runner.xsh +++ b/tools/mlir_bench/lora-runner.xsh @@ -16,7 +16,7 @@ SIZES = [8, 16, 32, 64, 128, 256, 512, 1024] ITERATIONS = 100 BENCH_RUNNER="tpp-run" -BENCH_FLAGS=f"-entry-point-result=void -e entry -seed 123 -n {ITERATIONS}".split() +RUNNER_FLAGS=f"-entry-point-result=void -e entry -seed 123 -n {ITERATIONS}".split() def build_lora_model(x_dyn_dim): @@ -52,14 +52,16 @@ def main(): model = build_lora_model(size) ov.save_model(model, model_xml) + BENCH_FLAGS=f"-m {model_xml} -d CPU -ip f32 -infer_precision f32 -hint none -nstreams 1 -nthreads 1".split() + def do_it(env_str): - out = $(env @(env_str) benchmark_app -m @(model_xml) -d CPU -niter @(ITERATIONS) -hint none -nstreams 1 -nthreads 1) - match = re.search(r"Average: +(\d.*) ms", out) + out = $(env @(env_str) benchmark_app @(BENCH_FLAGS) -niter @(ITERATIONS)) + match = re.search(r"Median: +(\d.*) ms", out) return float(match.group(1)) no_mlir_averages.append(do_it("OV_MLIR=0")) mlir_averages.append(do_it("OV_MLIR=1")) - raw_kernel_secs = $(env OV_MLIR=1 OV_MLIR_TPP=1 OV_MLIR_DEBUG=1 benchmark_app -m @(model_xml) -d CPU -niter 1 -hint none -nstreams 1 -nthreads 1 2>&1 | awk '/Source MLIR:/{flag=1; next} /Target LLVM:/{flag=0} flag' | grep -vE '^[-]+$' | tpp-run @(BENCH_FLAGS)) + raw_kernel_secs = $(env OV_MLIR=1 OV_MLIR_TPP=1 OV_MLIR_DEBUG=1 benchmark_app @(BENCH_FLAGS) -niter 1 2>&1 | awk '/Source MLIR:/{flag=1; next} /Target LLVM:/{flag=0} flag' | grep -vE '^[-]+$' | tpp-run @(RUNNER_FLAGS)) no_ov_averages.append(float(raw_kernel_secs) * 1000) print("SIZES", SIZES) From a48c9f101c9e87c9f12477a19023d83c29d7cc4e Mon Sep 17 00:00:00 2001 From: Adam Siemieniuk Date: Tue, 10 Sep 2024 15:00:57 +0200 Subject: [PATCH 036/121] LoRA manual MLIR model (#171) Adds manually written MLIR LoRA snippet and further parametrizes benchmark model creation. The MLIR snippet creates weights as constants to allow for compile-time packing. Function arguments and operations are kept in line with subgraph created by OV MLIR outlining. Benchmark model builders are parametrized to allow control over weights matrix and LoRA dimensions. The new model's performance is measured as 'manual MLIR' which evaluates pure MLIR performance with more optimal input IR compared to the current OV outlining. --- tools/mlir_bench/lora-runner.xsh | 92 +++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 19 deletions(-) diff --git a/tools/mlir_bench/lora-runner.xsh b/tools/mlir_bench/lora-runner.xsh index f4bdcfdd69c881..8cddf83ef058a1 100755 --- a/tools/mlir_bench/lora-runner.xsh +++ b/tools/mlir_bench/lora-runner.xsh @@ -10,25 +10,29 @@ from openvino_devtools.builder import OpFactory, outputs_to_nodes import numpy as np from pprint import pprint import re +from os import environ -SIZES = [8, 16, 32, 64, 128, 256, 512, 1024] +CONFIGS = [ + [8], [16], [32], [64], [128], [256], [512], [1024] +] ITERATIONS = 100 BENCH_RUNNER="tpp-run" RUNNER_FLAGS=f"-entry-point-result=void -e entry -seed 123 -n {ITERATIONS}".split() +DEBUG = environ.get("OV_MLIR_DEBUG", "0").lower() in ("true", "1", "on") -def build_lora_model(x_dyn_dim): +def build_ov_lora_model(input_dim=-1, weight_dim=2048, lora_dim=8): opset = OpFactory('opset13') #t40 = opset.Parameter({'shape': [-1, -1, 2048], 'element_type': 'f32'}, output_names=[{'x'}]) # Input data - t40 = opset.Parameter({'shape': [x_dyn_dim, 2048], 'element_type': 'f32'}, output_names=[{'x'}]) # Input data - t52 = opset.Parameter({'shape': [1, 8], 'element_type': 'f32'}, output_names=[{'alpha'}]) # LoRA alpha parameter + t40 = opset.Parameter({'shape': [input_dim, weight_dim], 'element_type': 'f32'}, output_names=[{'x'}]) # Input data + t52 = opset.Parameter({'shape': [1, lora_dim], 'element_type': 'f32'}, output_names=[{'alpha'}]) # LoRA alpha parameter - t48 = Constant(np.random.rand(2048, 2048).astype(np.float32)) # -> f32[2048,2048] # Original weight matrix W (usually it is compressed to bf16/f16/u8/u4 and represented as a sub-graph) - t50 = Constant(np.random.rand(8, 2048).astype(np.float32)) # -> f32[8,2048] # LoRA matrix A - t54 = Constant(np.random.rand(2048, 8).astype(np.float32)) # -> f32[2048,8] # LoRA matrix B + t48 = Constant(np.random.rand(weight_dim, weight_dim).astype(np.float32)) # -> f32[2048,2048] # Original weight matrix W (usually it is compressed to bf16/f16/u8/u4 and represented as a sub-graph) + t50 = Constant(np.random.rand(lora_dim, weight_dim).astype(np.float32)) # -> f32[8,2048] # LoRA matrix A + t54 = Constant(np.random.rand(weight_dim, lora_dim).astype(np.float32)) # -> f32[2048,8] # LoRA matrix B t49 = opset.MatMul([t40, t48], {'transpose_a': False, 'transpose_b': True}) # f32[?,?,2048], f32[2048,2048] -> f32[?,?,2048] t51 = opset.MatMul([t40, t50], {'transpose_a': False, 'transpose_b': True}) # f32[?,?,2048], f32[8,2048] -> f32[?,?,8] @@ -43,31 +47,81 @@ def build_lora_model(x_dyn_dim): return ov.Model(outputs_to_nodes(results), outputs_to_nodes(sinks), outputs_to_nodes(parameters)) +def build_mlir_lora_model(input_dim=-1, weight_dim=2048, lora_dim=8): + input_dim = '?' if input_dim == -1 else input_dim + mlir_model = f"\ +!inputType = tensor<{input_dim}x{weight_dim}xf32>\n\ +!loraAlphaType = tensor<1x{lora_dim}xf32>\n\ +!weightType = tensor<{weight_dim}x{weight_dim}xf32>\n\ +!loraMatAType = tensor<{lora_dim}x{weight_dim}xf32>\n\ +!loraMatBType = tensor<{weight_dim}x{lora_dim}xf32>\n\ +!loraResultType = tensor<{input_dim}x{lora_dim}xf32>\n\ +func.func @entry(%arg0: !loraAlphaType, %arg1: !inputType) -> !inputType {{\n\ + %cst = arith.constant 0.000000e+00 : f32\n\ + %weights = arith.constant dense<0.001000e+00> : !weightType\n\ + %loraA = arith.constant dense<0.002000e+00> : !loraMatAType\n\ + %loraB = arith.constant dense<0.003000e+00> : !loraMatBType\n\ + %0 = tensor.empty() : !loraResultType\n\ + %1 = linalg.fill ins(%cst : f32) outs(%0 : !loraResultType)\ + -> !loraResultType\n\ + %2 = linalg.matmul_transpose_b ins(%arg1, %loraA : !inputType, !loraMatAType)\ + outs(%1 : !loraResultType) -> !loraResultType\n\ + %collapsed = tensor.collapse_shape %arg0 [[0, 1]] : !loraAlphaType into tensor<{lora_dim}xf32>\n\ + %broadcasted = linalg.broadcast ins(%collapsed : tensor<{lora_dim}xf32>)\ + outs(%0 : !loraResultType) dimensions = [0]\n\ + %3 = linalg.mul ins(%2, %broadcasted : !loraResultType, !loraResultType)\ + outs(%0 : !loraResultType) -> !loraResultType\n\ + %4 = tensor.empty() : !inputType\n\ + %5 = linalg.fill ins(%cst : f32) outs(%4 : !inputType) -> !inputType\n\ + %6 = linalg.matmul_transpose_b ins(%3, %loraB : !loraResultType, !loraMatBType)\ + outs(%5 : !inputType) -> !inputType\n\ + %7 = linalg.matmul_transpose_b ins(%arg1, %weights : !inputType, !weightType)\ + outs(%5 : !inputType) -> !inputType\n\ + %8 = linalg.add ins(%7, %6 : !inputType, !inputType) outs(%4 : !inputType) -> !inputType\n\ + return %8 : !inputType\n\ +}}\n\ +" + return mlir_model + + def main(): no_mlir_averages = [] mlir_averages = [] no_ov_averages = [] - for size in SIZES: - model_xml = f"lora.{size}.xml" - model = build_lora_model(size) + manual_mlir_averages = [] + for config in CONFIGS: + model_desc = '.'.join(str(x) for x in config) + model_xml = f"lora.{model_desc}.xml" + model = build_ov_lora_model(*config) ov.save_model(model, model_xml) BENCH_FLAGS=f"-m {model_xml} -d CPU -ip f32 -infer_precision f32 -hint none -nstreams 1 -nthreads 1".split() - def do_it(env_str): - out = $(env @(env_str) benchmark_app @(BENCH_FLAGS) -niter @(ITERATIONS)) + def run_ov(env_str): + out = $(env @(env_str.split()) benchmark_app @(BENCH_FLAGS) -niter @(ITERATIONS)) match = re.search(r"Median: +(\d.*) ms", out) return float(match.group(1)) - no_mlir_averages.append(do_it("OV_MLIR=0")) - mlir_averages.append(do_it("OV_MLIR=1")) - - raw_kernel_secs = $(env OV_MLIR=1 OV_MLIR_TPP=1 OV_MLIR_DEBUG=1 benchmark_app @(BENCH_FLAGS) -niter 1 2>&1 | awk '/Source MLIR:/{flag=1; next} /Target LLVM:/{flag=0} flag' | grep -vE '^[-]+$' | tpp-run @(RUNNER_FLAGS)) - no_ov_averages.append(float(raw_kernel_secs) * 1000) - - print("SIZES", SIZES) + no_mlir_averages.append(run_ov("OV_MLIR=0")) + mlir_averages.append(run_ov("OV_MLIR=1")) + + def run_no_ov_mlir(env_str): + raw_kernel_secs = $(env @(env_str.split()) benchmark_app @(BENCH_FLAGS) -niter 1 2>&1 | awk '/Source MLIR:/{flag=1; next} /Target LLVM:/{flag=0} flag' | grep -vE '^[-]+$' | tpp-run @(RUNNER_FLAGS)) + return float(raw_kernel_secs) * 1000 + no_ov_averages.append(run_no_ov_mlir("OV_MLIR=1 OV_MLIR_TPP=1 OV_MLIR_DEBUG=1")) + + def run_manual_mlir(env_str): + mlir_model = build_mlir_lora_model(*config) + if DEBUG: + print(mlir_model) + raw_kernel_secs = $(echo @(mlir_model) | tpp-run @(RUNNER_FLAGS)) + return float(raw_kernel_secs) * 1000 + manual_mlir_averages.append(run_manual_mlir("")) + + print("CONFIGS", CONFIGS) print("OV NO-MLIR", no_mlir_averages) print("OV MLIR", mlir_averages) print("NO-OV MLIR", no_ov_averages) + print("MANUAL MLIR", manual_mlir_averages) if __name__ == "__main__": From fc319c4f511925c06aec5e94cb4d47c2b53875e4 Mon Sep 17 00:00:00 2001 From: Rolf Morel Date: Wed, 25 Sep 2024 18:15:35 +0200 Subject: [PATCH 037/121] [MLIR][TPP] Enable --lower-pack-unpack-without-transpose on LoRA benchmark (#173) Only gets applied to MANUAL MLIR runs as for these the constant weights will remain packed (applying it to OV-derived IR would undo all packing, also on the weights that are passed as an argument). Additionally, adds more sizes to test on. --- tools/mlir_bench/lora-runner.xsh | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/tools/mlir_bench/lora-runner.xsh b/tools/mlir_bench/lora-runner.xsh index 8cddf83ef058a1..8b4bdbe8663835 100755 --- a/tools/mlir_bench/lora-runner.xsh +++ b/tools/mlir_bench/lora-runner.xsh @@ -2,7 +2,7 @@ # xonsh can be installed with `pip install xonsh` # xonsh can then be run by invoking `python -m xonsh` -# this script in particular can be invoked with `python -m xonsh lora-benchmark.xsh` +# this script in particular can be invoked with `python -m xonsh lora-runner.xsh` import openvino as ov from openvino.runtime.op import Constant @@ -13,8 +13,10 @@ import re from os import environ +LORA_DIMS = [8, 16, 32, 64, 128] CONFIGS = [ - [8], [16], [32], [64], [128], [256], [512], [1024] + [8], [16], [32], [64], [128], [256], [512], [1024], + [2048], [4096], [8192] ] ITERATIONS = 100 @@ -84,7 +86,7 @@ func.func @entry(%arg0: !loraAlphaType, %arg1: !inputType) -> !inputType {{\n\ return mlir_model -def main(): +def full_run(lora_dim): no_mlir_averages = [] mlir_averages = [] no_ov_averages = [] @@ -92,7 +94,7 @@ def main(): for config in CONFIGS: model_desc = '.'.join(str(x) for x in config) model_xml = f"lora.{model_desc}.xml" - model = build_ov_lora_model(*config) + model = build_ov_lora_model(*config, lora_dim=lora_dim) ov.save_model(model, model_xml) BENCH_FLAGS=f"-m {model_xml} -d CPU -ip f32 -infer_precision f32 -hint none -nstreams 1 -nthreads 1".split() @@ -110,18 +112,24 @@ def main(): no_ov_averages.append(run_no_ov_mlir("OV_MLIR=1 OV_MLIR_TPP=1 OV_MLIR_DEBUG=1")) def run_manual_mlir(env_str): - mlir_model = build_mlir_lora_model(*config) + mlir_model = build_mlir_lora_model(*config, lora_dim=lora_dim) if DEBUG: print(mlir_model) - raw_kernel_secs = $(echo @(mlir_model) | tpp-run @(RUNNER_FLAGS)) + raw_kernel_secs = $(@(lambda: print(mlir_model)) | tpp-run @(RUNNER_FLAGS) --lower-pack-unpack-without-transpose) return float(raw_kernel_secs) * 1000 manual_mlir_averages.append(run_manual_mlir("")) print("CONFIGS", CONFIGS) print("OV NO-MLIR", no_mlir_averages) print("OV MLIR", mlir_averages) - print("NO-OV MLIR", no_ov_averages) - print("MANUAL MLIR", manual_mlir_averages) + print("NO-OV MLIR", list(round(x, 2) for x in no_ov_averages)) + print("MANUAL MLIR", list(round(x, 2) for x in manual_mlir_averages)) + + +def main(): + for lora_dim in LORA_DIMS: + print("lora_dim =", lora_dim) + full_run(lora_dim) if __name__ == "__main__": From 0b3fecad18f0a56cd1eebe26b11d4a6e0cd5cb84 Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Tue, 15 Oct 2024 16:33:54 +0200 Subject: [PATCH 038/121] GC-GPU integration (#169) * Initial gc-gpu integration Signed-off-by: dchigarev * disable gpu mode bu default Signed-off-by: dchigarev * Draft of forwarding cl queue Co-authored-by: Andrey Pavlenko Signed-off-by: dchigarev * Add tests with cl buffers Signed-off-by: dchigarev * add f16 tests to support dpas Signed-off-by: dchigarev * allign with new gc Signed-off-by: dchigarev * Align integration with new GC runtime Signed-off-by: dchigarev * Forward device information to mlir_op at model::compile() time Signed-off-by: dchigarev * do not 'wait()' before 'mlir::gpu_op' Signed-off-by: dchigarev * fix naming and put few 'vector::reserve()' Signed-off-by: dchigarev * correct 'graph-compiler.cmake' Signed-off-by: dchigarev * return cl_event to OV properly Signed-off-by: dchigarev * pass tensor vectors as is to MLIREvaluate::invoke()' Signed-off-by: dchigarev * address review comments Signed-off-by: dchigarev * move mlir-related properties to dev_api Signed-off-by: dchigarev * return handles from ocl impls Signed-off-by: dchigarev * fix graph-compiler.cmake Signed-off-by: dchigarev * fix cmake Signed-off-by: dchigarev * create event from ocl handle Signed-off-by: dchigarev * apply review suggestions Signed-off-by: dchigarev * assume there's one device per cl_context Signed-off-by: dchigarev --------- Signed-off-by: dchigarev Co-authored-by: Andrey Pavlenko --- cmake/graph-compiler.cmake | 15 +- src/common/transformations/CMakeLists.txt | 1 + .../include/transformations/mlir/convert.hpp | 3 +- .../src/transformations/mlir/convert.cpp | 41 ++- .../src/transformations/mlir/mlir_op.cpp | 197 +++++++++++- .../src/transformations/mlir/mlir_op.hpp | 52 ++- src/core/CMakeLists.txt | 8 +- .../openvino/runtime/internal_properties.hpp | 31 ++ .../transformation_pipeline.cpp | 2 +- src/plugins/intel_gpu/CMakeLists.txt | 6 +- .../include/intel_gpu/runtime/event.hpp | 2 + .../include/intel_gpu/runtime/memory.hpp | 2 + .../include/intel_gpu/runtime/stream.hpp | 4 +- .../intel_gpu/src/plugin/ops/mlir_op.cpp | 100 ++++-- .../src/plugin/transformations_pipeline.cpp | 9 +- .../src/runtime/ocl/ocl_base_event.hpp | 1 + .../intel_gpu/src/runtime/ocl/ocl_memory.hpp | 2 + .../intel_gpu/src/runtime/ocl/ocl_stream.cpp | 5 +- .../intel_gpu/src/runtime/ocl/ocl_stream.hpp | 3 +- .../opencl_helper_instance.hpp | 20 +- .../intel_gpu/tests/functional/CMakeLists.txt | 2 + .../mlir_op/models/matmul_64_128_f16.bin | Bin 0 -> 32768 bytes .../mlir_op/models/matmul_64_128_f16.xml | 83 +++++ .../mlir_op/models/matmul_64_128_f32.bin | Bin 0 -> 65536 bytes .../mlir_op/models/matmul_64_128_f32.xml | 83 +++++ .../tests/functional/mlir_op/sanity_tests.cpp | 304 ++++++++++++++++++ 26 files changed, 917 insertions(+), 59 deletions(-) rename src/plugins/intel_gpu/tests/{unit/test_utils => common}/opencl_helper_instance.hpp (81%) create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.bin create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.bin create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.xml create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index 30b9e5b6913d14..a0b3e7268758c1 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -4,6 +4,8 @@ if (NOT DEFINED GRAPH_COMPILER_LIBS) # versions, using find_package() first. If the package is not found, then using FetchContent. if (CMAKE_VERSION VERSION_LESS "3.24") find_package(GraphCompiler QUIET) + else () + set(GC_FETCH_CONTENT_ARGS FIND_PACKAGE_ARGS NAMES GraphCompiler) endif () if (NOT GraphCompiler_FOUND) @@ -13,12 +15,13 @@ if (NOT DEFINED GRAPH_COMPILER_LIBS) GC GIT_REPOSITORY https://github.com/intel/graph-compiler.git GIT_TAG main - FIND_PACKAGE_ARGS NAMES GraphCompiler + ${GC_FETCH_CONTENT_ARGS} ) - set(GC_ENABLE_OPT OFF) + set(GC_ENABLE_IMEX ${ENABLE_INTEL_GPU}) + set(GC_ENABLE_TOOLS OFF) set(GC_ENABLE_TEST OFF) - set(GC_ENABLE_DNNL OFF) + set(GC_ENABLE_DNNL_API OFF) set(GC_ENABLE_LEGACY OFF) set(GC_ENABLE_BINDINGS_PYTHON OFF) set(OV_BUILD_SHARED_LIBS_TMP ${BUILD_SHARED_LIBS}) @@ -32,7 +35,13 @@ if (NOT DEFINED GRAPH_COMPILER_LIBS) GcJitWrapper GcCpuRuntime ) + + if (ENABLE_INTEL_GPU) + list(APPEND GRAPH_COMPILER_LIBS GcGpuOclRuntime) + endif() + set_property(GLOBAL PROPERTY GRAPH_COMPILER_LIBS ${GRAPH_COMPILER_LIBS}) endif () get_target_property(GRAPH_COMPILER_INCLUDES GcInterface INTERFACE_INCLUDE_DIRECTORIES) +get_target_property(GRAPH_COMPILER_COMPILE_OPTIONS GcInterface INTERFACE_COMPILE_OPTIONS) diff --git a/src/common/transformations/CMakeLists.txt b/src/common/transformations/CMakeLists.txt index 3c9bcc05c6978c..c1fc4cf6efff5e 100644 --- a/src/common/transformations/CMakeLists.txt +++ b/src/common/transformations/CMakeLists.txt @@ -40,6 +40,7 @@ target_include_directories(${TARGET_NAME}_obj PRIVATE "${PUBLIC_HEADERS_DIR}" "${LLVM_INCLUDE_DIRS}" "${GRAPH_COMPILER_INCLUDES}") +target_compile_options(${TARGET_NAME}_obj PUBLIC ${GRAPH_COMPILER_COMPILE_OPTIONS}) add_tpp_mlir_includes(${TARGET_NAME}_obj) diff --git a/src/common/transformations/include/transformations/mlir/convert.hpp b/src/common/transformations/include/transformations/mlir/convert.hpp index 5fbcc3d13e2a6d..aed271d1baf38a 100644 --- a/src/common/transformations/include/transformations/mlir/convert.hpp +++ b/src/common/transformations/include/transformations/mlir/convert.hpp @@ -11,7 +11,8 @@ namespace ov { namespace pass { -void TRANSFORMATIONS_API transformMLIR(std::shared_ptr model); +void TRANSFORMATIONS_API transformMLIR(std::shared_ptr model, + std::shared_ptr loweringContext); } } diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index 60b86b263bfa14..8372cc86c4cda8 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -203,7 +203,10 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, // This pass converts a group of nodes into a single MLIROp -NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph, MlirMode mode) { +NodePtr ngraph_to_mlir_op(MLIRContext* context, + SubgraphPtr subgraph, + MlirMode mode, + std::shared_ptr loweringContext) { mlir::OwningOpRef module = ngraph_to_mlir(context, subgraph->inputs, subgraph->nodes, subgraph->outputs); const auto& inputs = subgraph->inputs; @@ -247,7 +250,7 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph, MlirMode m } return std::make_shared( subgraph->inputs, - std::make_shared(std::move(module), mode), + MLIREvaluate::create(std::move(module), mode, loweringContext), output_types, output_map ); @@ -269,17 +272,19 @@ void replace_subgraph(SubgraphPtr subgraph, NodePtr node) { class Partitioner : public ov::pass::ModelPass { MLIRContext* context; MlirMode mode; + std::shared_ptr loweringContext; public: OPENVINO_RTTI("Partitioner"); - Partitioner(MLIRContext* context, MlirMode mode) : + Partitioner(MLIRContext* context, MlirMode mode, std::shared_ptr loweringContext) : context(context), - mode(mode) + mode(mode), + loweringContext(loweringContext) {} bool run_on_model(const std::shared_ptr& model) override { SubgraphTracker tracker([this](SubgraphPtr subgraph) { - auto mlir_op = ngraph_to_mlir_op(context, subgraph, mode); + auto mlir_op = ngraph_to_mlir_op(context, subgraph, mode, loweringContext); replace_subgraph(subgraph, mlir_op); OPENVINO_MLIR_DEBUG_PRINT("Created MLIR op: " << mlir_op << "\n"); } @@ -293,7 +298,10 @@ class Partitioner : public ov::pass::ModelPass { }; -void injectMLIR(std::shared_ptr model, MLIRContext* context, MlirMode mode) { +void injectMLIR(std::shared_ptr model, + MLIRContext* context, + MlirMode mode, + std::shared_ptr loweringContext) { ov::pass::Manager manager; using namespace ov::op; manager.set_per_pass_validation(false); @@ -304,7 +312,7 @@ void injectMLIR(std::shared_ptr model, MLIRContext* context, MlirMode manager.register_pass>(); manager.register_pass(); manager.register_pass(); - manager.register_pass(context, mode); + manager.register_pass(context, mode, loweringContext); manager.run_passes(model); model->validate_nodes_and_infer_types(); } @@ -335,7 +343,7 @@ MLIRContext* get_shared_mlir_context(MlirMode mode) { } #ifdef GRAPH_COMPILER - if (mode == MLIR_MODE_GC) { + if (mode == MLIR_MODE_GC || mode == MLIR_MODE_GC_GPU) { OPENVINO_MLIR_DEBUG_PRINT("GC\n"); context = std::make_shared(gc::initCompilerAndGetDialects()); } else { @@ -386,7 +394,8 @@ MLIRContext* get_shared_mlir_context(MlirMode mode) { } // namespace -void ov::pass::transformMLIR(std::shared_ptr model) { +void ov::pass::transformMLIR(std::shared_ptr model, + std::shared_ptr loweringContext) { if(util::getenv_bool("OV_MLIR", true)) { const char *default_mode = #ifdef TPP_MLIR @@ -421,11 +430,23 @@ void ov::pass::transformMLIR(std::shared_ptr model) { "but OV_MLIR_MODE environment variable is set to GC."); #endif mode = MLIR_MODE_GC; + } else if (mode_str == "GC_GPU") { +#ifndef GRAPH_COMPILER + OPENVINO_THROW( + "[ ERROR ] OpenVINO wasn't compiled with GRAPH_COMPILER support, " + "but OV_MLIR_MODE environment variable is set to GC_GPU."); +#endif +#ifndef GC_USE_IMEX // GC_GPU requires IMEX support + OPENVINO_THROW( + "[ ERROR ] GraphCompiler wasn't compiled with IMEX support (-DGC_ENABLE_IMEX), " + "but OV_MLIR_MODE environment variable is set to GC_GPU."); +#endif + mode = MLIR_MODE_GC_GPU; } else { OPENVINO_ASSERT(mode_str == "DEFAULT"); mode = MLIR_MODE_DEFAULT; } - injectMLIR(model, get_shared_mlir_context(mode), mode); + injectMLIR(model, get_shared_mlir_context(mode), mode, loweringContext); } } diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index afe777ef949e6f..8ab1dd8fb25830 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -60,6 +60,13 @@ #ifdef GRAPH_COMPILER #include "gc/Transforms/Passes.h" + +#ifdef GC_USE_IMEX // GC_GPU requires IMEX support +#include "gc/Utils/Error.h" +#include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" +#include "openvino/runtime/intel_gpu/remote_properties.hpp" +#include "openvino/runtime/internal_properties.hpp" +#endif #endif namespace { @@ -225,12 +232,25 @@ std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext struct MemRefDescriptor { MemRefDescriptor() = default; - MemRefDescriptor (ov::Tensor tensor) + MemRefDescriptor (ov::Tensor tensor, const ov::Shape& module_input_shape) : allocated(tensor.data()), aligned(tensor.data()), offset(0), - shape(tensor.get_shape().begin(), tensor.get_shape().end()) { - strides.resize(tensor.get_shape().size()); + shape(module_input_shape.begin(), module_input_shape.end()) { + if (shape.size() != tensor.get_shape().size()) { + // validate that the shape difference is due to trailing '1's + for (size_t i = 0; i < shape.size(); ++i) { + if (shape[i] != tensor.get_shape()[i]) { + OPENVINO_THROW("Mismatch in shape sizes"); + } + } + for (size_t i = shape.size(); i < tensor.get_shape().size(); ++i) { + if (tensor.get_shape()[i] != 1) { + OPENVINO_THROW("Mismatch in shape sizes"); + } + } + } + strides.resize(shape.size()); const auto& byte_strides = tensor.get_strides(); auto element_size = tensor.get_element_type().size(); for (size_t i = 0; i < strides.size(); ++i) { @@ -241,6 +261,9 @@ struct MemRefDescriptor { } } + MemRefDescriptor (ov::Tensor tensor) + : MemRefDescriptor(tensor, tensor.get_shape()) {} + void* allocated; void* aligned; int64_t offset; @@ -267,6 +290,155 @@ namespace mlir { using namespace ::mlir; +std::shared_ptr MLIREvaluateBase::create(OwningOpRef module, + MlirMode mode, + std::shared_ptr loweringContext) { + switch (mode) { + #ifdef GC_USE_IMEX // GC_GPU requires IMEX support + case MLIR_MODE_GC_GPU: + return std::make_shared(std::move(module), loweringContext); + #endif + case MLIR_MODE_TPP: + case MLIR_MODE_GC: + case MLIR_MODE_DEFAULT: + return std::make_shared(std::move(module), mode); + default: + OPENVINO_THROW("Unsupported MLIR mode"); + } +} + +#ifdef GC_USE_IMEX // GC_GPU requires IMEX support + +cl_device_id extract_device_from_context(cl_context context) { + size_t devices_size; + cl_int err = clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &devices_size); + if (err != CL_SUCCESS) { + OPENVINO_THROW("Error getting context info: ", err); + } + if (devices_size / sizeof(cl_device_id) != 1) { + OPENVINO_THROW("Expected exactly one device in the context, got ", devices_size); + } + + cl_device_id devices; + err = clGetContextInfo(context, CL_CONTEXT_DEVICES, devices_size, &devices, NULL); + if (err != CL_SUCCESS) { + OPENVINO_THROW("Error getting device IDs: ", err); + } + + return devices; +} + +MLIREvaluateGcGPU::MLIREvaluateGcGPU(OwningOpRef _module, std::shared_ptr loweringContext) { + OPENVINO_MLIR_DEBUG_PRINT( + "[ DEBUG ] Source MLIR:\n" + "-----------------------------------------\n"); + OPENVINO_MLIR_DEBUG(_module->dump()); + OPENVINO_MLIR_DEBUG_PRINT( + "-----------------------------------------\n"); + + gc::gpu::OclModuleBuilderOpts opts; + OPENVINO_MLIR_DEBUG(opts.printIr = true); + gc::gpu::OclModuleBuilder builder(std::move(_module), opts); + + auto it = loweringContext->find(ov::intel_gpu::ocl_context.name()); + if (it == loweringContext->end()) { + OPENVINO_THROW("No cl_context provided for OpenCL execution"); + } + auto context = reinterpret_cast(it->second.as()); + // assuming there's always one device per context + auto device = extract_device_from_context(context); + + OPENVINO_MLIR_DEBUG_PRINT( + "[ DEBUG ] Target LLVM:\n" + "-----------------------------------------\n"); + if (auto mod = builder.build(device, context)) { + module = *mod; + } else { + OPENVINO_THROW("Failed to build gc::gpuOclModule module"); + } + OPENVINO_MLIR_DEBUG_PRINT( + "-----------------------------------------\n"); +}; + +bool MLIREvaluateGcGPU::invoke(const ov::TensorVector& inputs, ov::TensorVector& outputs, const ov::EvaluationContext& evaluationContext) { + gc::gpu::OclContext ctx = build_ocl_context(evaluationContext); + gc::gpu::StaticExecutor exec(module); + + auto it = evaluationContext.find(ov::internal::mlir_meta::is_kernel_arg_usm.name()); + if (it == evaluationContext.end()) { + OPENVINO_THROW("No is_kernel_arg_usm provided for OpenCL execution"); + } + std::vector arg_types = it->second.as>(); + + for (size_t i = 0; i < inputs.size(); ++i) { + exec.arg(inputs[i].data(), arg_types[i]); + } + for (size_t i = 0, j = inputs.size(); i < outputs.size(); ++i, ++j) { + exec.arg(outputs[i].data(), arg_types[j]); + } + exec(ctx); + maybe_set_result_event(evaluationContext, ctx); + return true; +} + +bool MLIREvaluateGcGPU::invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) { + gc::gpu::OclContext ctx = build_ocl_context(evaluationContext); + gc::gpu::DynamicExecutor exec(module); + + auto it = evaluationContext.find(ov::internal::mlir_meta::is_kernel_arg_usm.name()); + if (it == evaluationContext.end()) { + OPENVINO_THROW("No is_kernel_arg_usm provided for OpenCL execution"); + } + std::vector argTypes = it->second.as>(); + for (size_t i = 0; i < args.size(); i+=4) { + exec.arg( + /*alignedPtr=*/args[i], + /*rank=*/reinterpret_cast(args[i + 1]), + /*shape=*/reinterpret_cast(args[i + 2]), + /*strides=*/reinterpret_cast(args[i + 3]), + /*isUsm=*/argTypes[i] + ); + } + exec(ctx); + maybe_set_result_event(evaluationContext, ctx); + return true; +} + +void MLIREvaluateGcGPU::maybe_set_result_event(const ov::EvaluationContext& evaluationContext, gc::gpu::OclContext& ctx) { + // case with in-order queue where we don't need to return an event + if (ctx.lastEvent == nullptr) + return; + auto it = evaluationContext.find(ov::internal::mlir_meta::result_event.name()); + if (it == evaluationContext.end()) { + OPENVINO_THROW("No result_event provided for OpenCL execution"); + } + cl_event* ev = reinterpret_cast(it->second.as()); + *ev = ctx.lastEvent; +} + +gc::gpu::OclContext MLIREvaluateGcGPU::build_ocl_context(const ov::EvaluationContext& evaluationContext) { + auto it = evaluationContext.find(ov::intel_gpu::ocl_queue.name()); + if (it == evaluationContext.end()) { + OPENVINO_THROW("No queue provided for OpenCL execution"); + } + cl_command_queue queue = reinterpret_cast(it->second.as()); + + uint32_t waitListLen = 0; + std::vector waitList; + bool foundWaitList = false; + + it = evaluationContext.find(ov::internal::mlir_meta::wait_list.name()); + if (it != evaluationContext.end()) { + waitList = it->second.as>(); + waitListLen = waitList.size(); + foundWaitList = true; + } + + return gc::gpu::OclContext(module->runtime, queue, /*createEvents=*/foundWaitList, + waitListLen, reinterpret_cast(waitList.data())); +} + +#endif // GC_USE_IMEX MLIREvaluate::MLIREvaluate(OwningOpRef _module, MlirMode mode) : module(std::move(_module)) { @@ -304,7 +476,7 @@ MLIREvaluate::MLIREvaluate(OwningOpRef _module, MlirMode mode) : } } -bool MLIREvaluate::invoke_packed(std::vector& args) { +bool MLIREvaluate::invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) { auto invocationResult = engine->invokePacked("entry", args); if (invocationResult) { llvm::errs() << "JIT invocation failed\n"; @@ -313,7 +485,7 @@ bool MLIREvaluate::invoke_packed(std::vector& args) { return true; } -MLIROp::MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types, const DimensionsMap& dimensions_map) +MLIROp::MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types, const DimensionsMap& dimensions_map) : Op(args), engine(engine), output_types(output_types), @@ -332,10 +504,15 @@ NodePtr MLIROp::clone_with_new_inputs(const ov::OutputVector& new_args) const { return std::make_shared(new_args, engine, output_types, dimensions_map); } -bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const { +bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs, const ov::EvaluationContext& evaluationContext) const { + if (!engine->requires_packed_args()) { + return engine->invoke(inputs, outputs, evaluationContext); + } + std::vector memref_args; for (size_t i = 0; i < inputs.size(); ++i) { - memref_args.push_back(MemRefDescriptor(inputs[i])); + auto& initial_shape = get_input_shape(i); + memref_args.push_back(MemRefDescriptor(inputs[i], initial_shape)); } for (size_t i = 0; i < outputs.size(); ++i) { // TODO: Optimize by adding all dimensions to dimensions_map, not only dynamic @@ -362,7 +539,11 @@ bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) }); //std::cerr << "[ INFO ] Running kernel in MLIROp::evaluate\n"; - return engine->invoke_packed(args); + return engine->invoke_packed(args, evaluationContext); +} + +bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const { + return evaluate(outputs, inputs, ov::EvaluationContext()); } bool MLIROp::has_evaluate() const { diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.hpp b/src/common/transformations/src/transformations/mlir/mlir_op.hpp index a0ffc58713c30e..1429f8659bf2b8 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.hpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.hpp @@ -11,9 +11,14 @@ #include "mlir/ExecutionEngine/OptUtils.h" #include "openvino/op/op.hpp" +#include "openvino/core/shape.hpp" #include "convert_common.hpp" +#ifdef GC_USE_IMEX // GC_GPU requires IMEX support +#include "gc/Utils/Error.h" +#include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" +#endif namespace ov { namespace mlir { @@ -26,18 +31,54 @@ using ::mlir::ModuleOp; enum MlirMode { MLIR_MODE_TPP, MLIR_MODE_GC, + MLIR_MODE_GC_GPU, MLIR_MODE_DEFAULT, }; +class MLIROp; -class MLIREvaluate { +class MLIREvaluateBase { +public: + static std::shared_ptr create(OwningOpRef module, + MlirMode mode, + std::shared_ptr ex_context); + + virtual bool requires_packed_args() const = 0; + // ::invoke() doesn't require any args preprocessing so we can pass tensors as is + virtual bool invoke(const ov::TensorVector& inputs, ov::TensorVector& outputs, const ov::EvaluationContext& evaluationContext) = 0; + virtual bool invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) = 0; + virtual ~MLIREvaluateBase() = default; +}; + +#ifdef GC_USE_IMEX // GC_GPU requires IMEX support + +class MLIREvaluateGcGPU : public MLIREvaluateBase { + std::shared_ptr module; + +public: + MLIREvaluateGcGPU(OwningOpRef _module, std::shared_ptr loweringContext); + + bool requires_packed_args() const override { return !module->isStatic; } + bool invoke(const ov::TensorVector& inputs, ov::TensorVector& outputs, const ov::EvaluationContext& evaluationContext) override; + bool invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) override; + +private: + gc::gpu::OclContext build_ocl_context(const ov::EvaluationContext& evaluationContext); + static void maybe_set_result_event(const ov::EvaluationContext& evaluationContext, gc::gpu::OclContext& ctx); +}; + +#endif // GC_USE_IMEX + +class MLIREvaluate : public MLIREvaluateBase { OwningOpRef module; // FIXME: needs to be kept? std::unique_ptr engine; public: MLIREvaluate(OwningOpRef _module, MlirMode mode); - bool invoke_packed(std::vector& args); + bool requires_packed_args() const override { return true; } + bool invoke(const ov::TensorVector& inputs, ov::TensorVector& outputs, const ov::EvaluationContext& evaluationContext) override { return false; }; + bool invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) override; }; @@ -46,7 +87,7 @@ using DimensionsMap = std::vector>>; class OPENVINO_API MLIROp : public ov::op::Op { - std::shared_ptr engine; + std::shared_ptr engine; OVOutputTypes output_types; DimensionsMap dimensions_map; @@ -54,10 +95,13 @@ class OPENVINO_API MLIROp : public ov::op::Op { OPENVINO_OP("MLIROp"); - MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types, const DimensionsMap& dimensions_map); + MLIROp(const ov::OutputVector& args, std::shared_ptr engine, + const OVOutputTypes& output_types, const DimensionsMap& dimensions_map); void validate_and_infer_types() override; NodePtr clone_with_new_inputs(const ov::OutputVector& new_args) const override; bool evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const override; + bool evaluate(ov::TensorVector& output_values, const ov::TensorVector& input_values, + const ov::EvaluationContext& evaluationContext) const override; bool has_evaluate() const override; }; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 23e40625317e9f..b2bce106a48ec9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -48,7 +48,13 @@ target_include_directories(openvino_core_dev INTERFACE $ $ $ - $) + $ + # HACK: to make gpu properties from 'src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp' + # available in 'transformations/.../mlir_op.cpp'. Need to figure out something better. + $ + # HACK: to make mlir properties from 'src/inference/dev_api/openvino/runtime/internal_properties.hpp' + # available in 'transformations/.../mlir_op.cpp'. Need to figure out something better. + $) target_include_directories(openvino_core_dev SYSTEM INTERFACE $:$>>) diff --git a/src/inference/dev_api/openvino/runtime/internal_properties.hpp b/src/inference/dev_api/openvino/runtime/internal_properties.hpp index 88f3be80643da5..f582f796cebf65 100644 --- a/src/inference/dev_api/openvino/runtime/internal_properties.hpp +++ b/src/inference/dev_api/openvino/runtime/internal_properties.hpp @@ -169,5 +169,36 @@ static constexpr Property key_cache_quan */ static constexpr Property value_cache_quant_mode{"VALUE_CACHE_QUANT_MODE"}; +/* +* @brief Namespace for properties related to MLIR operations within the GPU plugin. + * These properties are used as evaluation context parameters for MLIR operations, + * assisting in managing events, result tracking, and kernel argument types. + */ +namespace mlir_meta { + +/** + * @brief This key identifies a list of cl_event to wait for a kernel execution. + * @ingroup ov_dev_api_plugin_mlir_meta_api + */ +static constexpr Property> wait_list{"EVENTS_WAIT_LIST"}; + +/** + * @brief This key identifies a pointer to a cl_enevt that should be set with + * the result cl_event of a kernel execution. Example: + * @code + * cl_event result_event = launchModuleAndGetEvent(); + * cl_event* ev = evaluationContext[ov::internal::mlir_meta::result_event.name()].as(); + * *ev = result_event; + * @ingroup ov_dev_api_plugin_mlir_meta_api + */ +static constexpr Property result_event{"RESULT_EVENT"}; + +/** + * @brief This key identifies whether the kernel argument at [i] position is USM pointer + * @ingroup ov_dev_api_plugin_mlir_meta_api + */ +static constexpr Property> is_kernel_arg_usm{"IS_KERNEL_ARG_USM"}; + +} // namespace mlir_meta } // namespace internal } // namespace ov diff --git a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp index c35fdb828c12ca..c1e2e530984a84 100644 --- a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp +++ b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp @@ -908,7 +908,7 @@ void Transformations::PreLpt(const std::vector& defaultPrecis CPU_REGISTER_PASS_COMMON(manager, ov::pass::ConstantFolding); CPU_REGISTER_PASS_COMMON(manager, ov::pass::LoraSubgraphFusion); - ov::pass::transformMLIR(model); + ov::pass::transformMLIR(model, std::make_shared()); manager.run_passes(model); } diff --git a/src/plugins/intel_gpu/CMakeLists.txt b/src/plugins/intel_gpu/CMakeLists.txt index d7285bfa2d8101..f66d9f0ce33c78 100644 --- a/src/plugins/intel_gpu/CMakeLists.txt +++ b/src/plugins/intel_gpu/CMakeLists.txt @@ -71,10 +71,12 @@ ov_add_plugin(NAME ${TARGET_NAME} target_compile_options(${TARGET_NAME} PRIVATE $<$:$,/Os,-Os>>) -target_link_libraries(${TARGET_NAME} PRIVATE openvino_intel_gpu_graph openvino::pugixml) +target_link_libraries( + ${TARGET_NAME} PRIVATE openvino_intel_gpu_graph openvino::pugixml ${GRAPH_COMPILER_LIBS}) target_include_directories(${TARGET_NAME} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/include/) + ${CMAKE_CURRENT_SOURCE_DIR}/include/ + "${GRAPH_COMPILER_INCLUDES}") ov_set_threading_interface_for(${TARGET_NAME}) diff --git a/src/plugins/intel_gpu/include/intel_gpu/runtime/event.hpp b/src/plugins/intel_gpu/include/intel_gpu/runtime/event.hpp index 3dbe51b146c204..9f17992bec7916 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/runtime/event.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/runtime/event.hpp @@ -35,6 +35,8 @@ struct event { // returns true if handler has been successfully added bool add_event_handler(event_handler handler, void* data); + // return a handle to an underlying event implementation (i.e. cl_event for OpenCL) + virtual void* get_handle() { return nullptr; } std::vector get_profiling_info(); diff --git a/src/plugins/intel_gpu/include/intel_gpu/runtime/memory.hpp b/src/plugins/intel_gpu/include/intel_gpu/runtime/memory.hpp index 78ff77c09bc37f..4cae863a15e894 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/runtime/memory.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/runtime/memory.hpp @@ -52,6 +52,8 @@ struct memory { virtual event::ptr fill(stream& stream, const std::vector& dep_events = {}, bool blocking = true) = 0; // only supports gpu_usm virtual void* buffer_ptr() const { return nullptr; } + // Returns the handle to the underlying memory object (e.g. cl_mem for OpenCL) + virtual void* get_handle() const { return nullptr; } size_t size() const { return _bytes_count; } size_t count() const { return _layout.count(); } diff --git a/src/plugins/intel_gpu/include/intel_gpu/runtime/stream.hpp b/src/plugins/intel_gpu/include/intel_gpu/runtime/stream.hpp index 71ead2c0473957..8809fcc23bf9f6 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/runtime/stream.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/runtime/stream.hpp @@ -65,10 +65,12 @@ class stream { virtual event::ptr group_events(std::vector const& deps) = 0; virtual void wait_for_events(const std::vector& events) = 0; virtual event::ptr create_user_event(bool set) = 0; - virtual event::ptr create_base_event() = 0; + virtual event::ptr create_base_event(void* handle = nullptr) = 0; virtual event::ptr aggregate_events(const std::vector& events, bool group = false, bool is_output = false); QueueTypes get_queue_type() const { return m_queue_type; } + // Returns the handle to the underlying stream object (e.g. cl_command_queue for OpenCL) + virtual void* get_handle() const { return nullptr; } SyncMethods get_sync_method() const { return m_sync_method; } static QueueTypes detect_queue_type(engine_types engine_type, void* queue_handle); diff --git a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp index 49d123bf717449..248034565a07ed 100644 --- a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp +++ b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp @@ -8,6 +8,10 @@ #include "intel_gpu/plugin/program_builder.hpp" #include "intel_gpu/primitives/generic_primitive.hpp" +#include "openvino/runtime/intel_gpu/ocl/ocl_wrapper.hpp" +#include "openvino/runtime/intel_gpu/remote_properties.hpp" +#include "openvino/runtime/internal_properties.hpp" + namespace ov { namespace op { namespace mlir { @@ -25,36 +29,90 @@ void CreateMLIRSubgraphOp(ProgramBuilder& p, const std::shared_ptr& inputs, const std::vector& outputs) { - // Synchronization as evalute() may be a CPU code - if (stream.get_queue_type() == cldnn::QueueTypes::out_of_order) { - for (auto& ev : dependent_events) { - ev->wait(); + ov::TensorVector input_gpu_tensors; + ov::TensorVector output_gpu_tensors; + std::vector is_usm_ptr; + input_gpu_tensors.reserve(inputs.size()); + output_gpu_tensors.reserve(outputs.size()); + is_usm_ptr.reserve(inputs.size() + outputs.size()); + + auto process_buffer = [&stream, &is_usm_ptr](cldnn::memory::ptr mem, ov::TensorVector& tensors) { + switch (mem->get_allocation_type()) { + case cldnn::allocation_type::cl_mem: { + if (void* cl_buff = mem->get_handle()) { + tensors.push_back(make_tensor(mem->get_layout(), cl_buff)); + is_usm_ptr.push_back(false); + } else { + OPENVINO_THROW("Memory handle is null for cl_mem"); + } + break; + } + case cldnn::allocation_type::usm_host: + case cldnn::allocation_type::usm_shared: + case cldnn::allocation_type::usm_device: { + auto usm_ptr = mem->buffer_ptr(); + // Seems to only occur with Out-Of-Order queues sometimes. Can't reproduce this anymore, uncomment if needed. + // HACK: force move to device, can we do better than this? + // auto gpu_buff = dynamic_cast(mem.get()); + // auto& usm_helper = gpu_buff->get_buffer().getUsmHelper(); + // usm_helper.enqueue_memcpy( + // dynamic_cast(stream).get_cl_queue(), + // usm_ptr, + // usm_ptr, + // mem->get_layout().bytes_count()); + tensors.push_back(make_tensor(mem->get_layout(), usm_ptr)); + is_usm_ptr.push_back(true); + break; + } + default: + OPENVINO_THROW("Unsupported memory type"); } - } else { - stream.finish(); - } + }; - cldnn::event::ptr ev = stream.create_user_event(false); + for (size_t i = 0; i < inputs.size(); i++) { + process_buffer(inputs[i], input_gpu_tensors); + } - ov::TensorVector input_host_tensors; - ov::TensorVector output_host_tensors; + for (size_t i = 0; i < outputs.size(); i++) { + process_buffer(outputs[i], output_gpu_tensors); + } - for (size_t i = 0; i < inputs.size(); i++) - input_host_tensors.push_back(make_tensor(inputs[i]->get_layout(), inputs[i]->lock(stream, cldnn::mem_lock_type::read))); + ov::EvaluationContext meta; + if (void* queue = stream.get_handle()) { + meta.insert(ov::intel_gpu::ocl_queue(queue)); + } else { + OPENVINO_THROW("Unsupported queue type"); + } + meta.insert(ov::internal::mlir_meta::is_kernel_arg_usm(is_usm_ptr)); - for (size_t i = 0; i < outputs.size(); i++) - output_host_tensors.push_back(make_tensor(outputs[i]->get_layout(), outputs[i]->lock(stream, cldnn::mem_lock_type::write))); + std::vector events_list; + cl_event* result_event = nullptr; + if (stream.get_queue_type() == cldnn::QueueTypes::out_of_order) { + events_list.reserve(dependent_events.size() + 1); + for (auto& ev : dependent_events) { + if (void* cl_ev = ev->get_handle()) { + events_list.push_back(cl_ev); + } else { + OPENVINO_THROW("Unsupported event type"); + } + } + meta.insert(ov::internal::mlir_meta::wait_list(events_list)); + // 'cl_event' is a pointer itself, that's why we pass pointer to a pointer here. + meta.insert(ov::internal::mlir_meta::result_event(reinterpret_cast(result_event))); + } - OPENVINO_ASSERT(op->evaluate(output_host_tensors, input_host_tensors), + OPENVINO_ASSERT(op->evaluate( + output_gpu_tensors, input_gpu_tensors, meta), "[GPU] Couldn't execute MLIROp ", op->get_friendly_name()); - for (size_t i = 0; i < inputs.size(); i++) - inputs[i]->unlock(stream); - - for (size_t i = 0; i < outputs.size(); i++) - outputs[i]->unlock(stream); + cldnn::event::ptr ev; + if (stream.get_queue_type() == cldnn::QueueTypes::out_of_order) { + OPENVINO_ASSERT(result_event != nullptr, "Result cl_event is not set"); + ev = stream.create_base_event(*result_event); + } else { + ev = stream.create_user_event(true); + } - ev->set(); return ev; }; cldnn::generic_primitive::shape_infer_function shape_infer_f = [&op]( diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index 4b0a5841da7677..72280e0003e4dc 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -1548,7 +1548,14 @@ void TransformationsPipeline::apply(std::shared_ptr func) { manager.register_pass(); - ov::pass::transformMLIR(func); + auto loweringContext = std::make_shared(); + auto it = m_context->get_property().find(ov::intel_gpu::ocl_context.name()); + if (it != m_context->get_property().end()) { + // We assume here that there's only one device per context and that an + // actual device will be extracted later by the 'mlir_op'. + loweringContext->insert(ov::intel_gpu::ocl_context(it->second.as())); + } + ov::pass::transformMLIR(func, loweringContext); // This is supposed to be the last pass to ensure that we don't have name collisions until // GPU plugin stops using friendly names for program creation diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_base_event.hpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_base_event.hpp index 699948ccd8843d..179efe5cffd7cd 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_base_event.hpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_base_event.hpp @@ -26,6 +26,7 @@ struct ocl_base_event : public event { explicit ocl_base_event(uint64_t queue_stamp = 0) : event(), _queue_stamp(queue_stamp) { } uint64_t get_queue_stamp() const { return _queue_stamp; } virtual cl::Event& get() = 0; + void* get_handle() override { return static_cast(get().get()); } protected: uint64_t _queue_stamp = 0; diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_memory.hpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_memory.hpp index aaf10dc55f55d0..a0cf7ad2dfc05a 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_memory.hpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_memory.hpp @@ -42,6 +42,8 @@ struct gpu_buffer : public lockable_gpu_mem, public memory { void* buffer_ptr() const override { return get_buffer().get(); } + void* get_handle() const override { return static_cast(get_buffer().get()); } + event::ptr copy_from(stream& stream, const void* data_ptr, size_t src_offset, size_t dst_offset, size_t size, bool blocking) override; event::ptr copy_from(stream& stream, const memory& src_mem, size_t src_offset, size_t dst_offset, size_t size, bool blocking) override; diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.cpp index f5e9b74a5e681b..fea87172ac0670 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.cpp @@ -357,8 +357,11 @@ event::ptr ocl_stream::create_user_event(bool set) { return std::make_shared(_engine.get_cl_context(), set); } -event::ptr ocl_stream::create_base_event() { +event::ptr ocl_stream::create_base_event(void* handle) { cl::Event ret_ev; + if (handle) { + ret_ev = reinterpret_cast(handle); + } return std::make_shared(ret_ev, ++_queue_counter); } diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.hpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.hpp index b9c51ccb046508..214f173e9718b3 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.hpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.hpp @@ -18,6 +18,7 @@ namespace ocl { class ocl_stream : public stream { public: const ocl_queue_type& get_cl_queue() const { return _command_queue; } + void* get_handle() const override { return static_cast(get_cl_queue().get()); } ocl_stream(const ocl_engine& engine, const ExecutionConfig& config); ocl_stream(const ocl_engine &engine, const ExecutionConfig& config, void *handle); @@ -46,7 +47,7 @@ class ocl_stream : public stream { void wait_for_events(const std::vector& events) override; void enqueue_barrier() override; event::ptr create_user_event(bool set) override; - event::ptr create_base_event() override; + event::ptr create_base_event(void* handle = nullptr) override; const cl::UsmHelper& get_usm_helper() const { return _engine.get_usm_helper(); } diff --git a/src/plugins/intel_gpu/tests/unit/test_utils/opencl_helper_instance.hpp b/src/plugins/intel_gpu/tests/common/opencl_helper_instance.hpp similarity index 81% rename from src/plugins/intel_gpu/tests/unit/test_utils/opencl_helper_instance.hpp rename to src/plugins/intel_gpu/tests/common/opencl_helper_instance.hpp index 6963d86e911bd7..1577fc4576039f 100644 --- a/src/plugins/intel_gpu/tests/unit/test_utils/opencl_helper_instance.hpp +++ b/src/plugins/intel_gpu/tests/common/opencl_helper_instance.hpp @@ -23,8 +23,7 @@ struct OpenCL { bool _supports_usm; bool _out_of_order_queue; - OpenCL(bool out_of_order_queue = true) - { + OpenCL(bool out_of_order_queue = true) { // get Intel iGPU OCL device, create context and queue { static constexpr auto INTEL_PLATFORM_VENDOR = "Intel(R) Corporation"; @@ -71,8 +70,7 @@ struct OpenCL { } } - OpenCL(cl::Device device, bool out_of_order_queue = true) - { + OpenCL(cl::Device device, bool out_of_order_queue = true) { cl_uint n = 0; cl_int err = clGetPlatformIDs(0, NULL, &n); checkStatus(err, "clGetPlatformIDs"); @@ -95,6 +93,20 @@ struct OpenCL { _queue = cl::CommandQueue(_context, _device, props); } + OpenCL(cl_context context, bool out_of_order_queue = true) + : _out_of_order_queue(out_of_order_queue) { + _context = cl::Context(context, true); + _device = cl::Device(_context.getInfo()[0].get(), true); + + cl_command_queue_properties props = _out_of_order_queue ? CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE : CL_NONE; + _queue = cl::CommandQueue(_context, _device, props); + + auto extensions = _device.getInfo(); + _supports_usm = extensions.find("cl_intel_unified_shared_memory") != std::string::npos; + + _usm_helper = std::make_shared(_context, _device, _supports_usm); + } + void releaseOclImage(std::shared_ptr image) { checkStatus(clReleaseMemObject(*image), "clReleaseMemObject"); } diff --git a/src/plugins/intel_gpu/tests/functional/CMakeLists.txt b/src/plugins/intel_gpu/tests/functional/CMakeLists.txt index 32aff2bc4f0285..07957020fc40ad 100644 --- a/src/plugins/intel_gpu/tests/functional/CMakeLists.txt +++ b/src/plugins/intel_gpu/tests/functional/CMakeLists.txt @@ -12,6 +12,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") ov_add_compiler_flags(/wd4305) endif() +list(APPEND DEFINES TEST_MODELS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/mlir_op/models") list(APPEND DEFINES TEST_CUSTOM_OP_CONFIG_PATH="${CMAKE_CURRENT_SOURCE_DIR}/custom_op/custom_op.xml") ov_add_test_target( @@ -24,6 +25,7 @@ ov_add_test_target( INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} $/include/ + $ ${TEST_COMMON_INCLUDE_DIR} DEFINES ${DEFINES} diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.bin b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.bin new file mode 100644 index 0000000000000000000000000000000000000000..5e8f00c71c2001838d6e0910638f518bc1022590 GIT binary patch literal 32768 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 M+68x3r=ZaP0S`-);s5{u literal 0 HcmV?d00001 diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml new file mode 100644 index 00000000000000..ede68f3452a1c2 --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml @@ -0,0 +1,83 @@ + + + + + + + + 64 + 128 + + + + + + + + 128 + 128 + + + + + + + + 64 + 128 + + + 128 + 128 + + + + + 64 + 128 + + + + + + + + 64 + 128 + + + 64 + 128 + + + + + 64 + 128 + + + + + + + 64 + 128 + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.bin b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.bin new file mode 100644 index 0000000000000000000000000000000000000000..0d0176d36fc40c5259635cd63d28e9f87c4325ab GIT binary patch literal 65536 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 + + + + + + + 64 + 128 + + + + + + + + 128 + 128 + + + + + + + + 64 + 128 + + + 128 + 128 + + + + + 64 + 128 + + + + + + + + 64 + 128 + + + 64 + 128 + + + + + 64 + 128 + + + + + + + 64 + 128 + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp new file mode 100644 index 00000000000000..8646fa705309df --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp @@ -0,0 +1,304 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// +#include "openvino/frontend/extension.hpp" + +#include + +#include "common_test_utils/file_utils.hpp" +#include "common_test_utils/test_assertions.hpp" +#include "openvino/runtime/core.hpp" +#include "openvino/util/file_util.hpp" +#include + +#include +#include +#include "opencl_helper_instance.hpp" +#include "openvino/core/preprocess/pre_post_process.hpp" + +using testing::ElementsAreArray; + +static std::string model_full_path(const char* path) { + return ov::util::make_path(TEST_MODELS_DIR, path); +} + +template +static void multiply_matrices_and_add_a(const std::vector& matrix_a, const std::vector& matrix_b, + std::vector& result, size_t rows_a, size_t cols_a, size_t cols_b) { + // Initialize the result matrix with zero values (f32 accumulator) + std::vector tmp(result.size(), 0.0f); + + // Matrix multiplication logic using linear indexing + for (size_t i = 0; i < rows_a; ++i) { + for (size_t j = 0; j < cols_b; ++j) { + for (size_t k = 0; k < cols_a; ++k) { + tmp[i * cols_b + j] += matrix_a[i * cols_a + k] * matrix_b[k * cols_b + j]; + } + } + } + + for (size_t i = 0; i < result.size(); i++) { + result[i] = tmp[i]; // cast back to T(possibly f16) + } + + for (size_t i = 0; i < matrix_a.size(); i++) { + result[i] += matrix_a[i]; + } +} + +template +static std::vector read_float_array_from_binary_file(const std::string& filename, size_t float_size = 4) { + // Open the binary file in input mode and binary mode + std::ifstream input_file(filename, std::ios::binary); + + // Check if the file was successfully opened + if (!input_file.is_open()) { + std::cerr << "Error: Could not open file " << filename << std::endl; + return {}; + } + + // Move the cursor to the end to determine the size of the file + input_file.seekg(0, std::ios::end); + std::streamsize file_size = input_file.tellg(); + input_file.seekg(0, std::ios::beg); + + // Calculate the number of floats in the file + std::size_t num_floats = file_size / float_size; + + // Create a vector to store the floats + std::vector float_array(num_floats); + + // Read the floats from the file into the vector + if (num_floats > 0) { + input_file.read(reinterpret_cast(float_array.data()), file_size); + } + + // Close the file + input_file.close(); + + return float_array; +} + +template +static ov::Tensor allocate_usm_tensor( + ov::intel_gpu::ocl::ClContext& oclContext, OpenCL* oclInstance, const ov::Shape& shape, + ov::element::Type type, std::vector &input_values) { + cl_int err; + size_t byte_size = shape_size(shape) * type.bitwidth() / 8; + + void* usm_ptr = oclInstance->_usm_helper->allocate_device( + /*properties=*/nullptr, + /*size=*/byte_size, + /*alignment=*/0, + /*err_code_return=*/&err); + std::cout << "allocated: " << usm_ptr << std::endl; + + err = oclInstance->_usm_helper->enqueue_memcpy( + oclInstance->_queue, + /*dst=*/usm_ptr, + /*src=*/input_values.data(), + byte_size, + /*blocking=*/true, + /*wait_list=*/nullptr, + /*ret_event=*/nullptr); + + return oclContext.create_tensor(type, shape, usm_ptr); +} + +template +static ov::Tensor allocate_cl_tensor( + ov::intel_gpu::ocl::ClContext& oclContext, OpenCL* oclInstance, const ov::Shape& shape, + ov::element::Type type, std::vector &input_values, std::vector& keep_alive) { + cl_int err; + size_t byte_size = shape_size(shape) * type.bitwidth() / 8; + + keep_alive.push_back( + cl::Buffer(oclInstance->_context, CL_MEM_READ_WRITE, (cl::size_type)byte_size, NULL, &err)); + + void* mappedPtr = oclInstance->_queue.enqueueMapBuffer(keep_alive.back(), + CL_TRUE, + CL_MAP_WRITE, + 0, + (cl::size_type)byte_size); + + memcpy(mappedPtr, input_values.data(), byte_size); + + oclInstance->_queue.enqueueUnmapMemObject(keep_alive.back(), mappedPtr); + + return oclContext.create_tensor(type, shape, keep_alive.back().get()); +} + +template +static std::vector broadcast_vector(const std::vector& v, size_t new_size) { + std::vector result; + result.reserve(new_size); + + size_t original_size = v.size(); + + if (original_size == 0) { + throw std::invalid_argument("Original vector size must be greater than 0."); + } + + // Fill the result vector by repeating the input vector + for (size_t i = 0; i < new_size; ++i) { + result.push_back(v[i % original_size]); + } + + return result; +} + +template +static std::map allocate_input_tensors( + ov::CompiledModel& compiledModel, + std::map> &inputValues, bool use_usm, std::vector& keep_alive) { + auto context = compiledModel.get_context(); + auto& oclContext = static_cast(context); + auto oclInstance = std::make_shared(oclContext.get()); + + std::map input_tensors; + for (const auto& input : compiledModel.inputs()) { + auto shape = input.get_shape(); + auto size = ov::shape_size(shape); + std::vector input_values = broadcast_vector(inputValues[input.get_index()], size); + ov::Tensor tensor; + if (use_usm) { + tensor = allocate_usm_tensor(oclContext, oclInstance.get(), shape, input.get_element_type(), input_values); + } else { + tensor = allocate_cl_tensor(oclContext, oclInstance.get(), shape, input.get_element_type(), input_values, keep_alive); + } + input_tensors.emplace(input.get_index(), tensor); + } + return input_tensors; +} + +TEST(MLIRExecution, SimpleMatmulf32) { + if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") + GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + + ov::Core core; + auto model = core.read_model( + model_full_path("matmul_64_128_f32.xml")); + + ov::AnyMap device_config; + device_config[ov::hint::performance_mode.name()] = ov::hint::PerformanceMode::THROUGHPUT; + device_config[ov::enable_profiling.name()] = false; + device_config.emplace(ov::hint::inference_precision("f32")); + + auto compiled_model = core.compile_model(model, "GPU", device_config); + + std::map> input_values_map; + input_values_map.emplace(0, std::vector(1, 0.5f)); + + std::vector keep_alive; + + auto input_tensors = allocate_input_tensors(compiled_model, input_values_map, true, keep_alive); + + auto infer_req = compiled_model.create_infer_request(); + for (const auto& input : input_tensors) { + infer_req.set_input_tensor(input.first, input.second); + } + infer_req.infer(); + + auto computed = infer_req.get_output_tensor(0); + float* result = reinterpret_cast(computed.data()); + + // compute reference result + std::vector matrix_a = broadcast_vector(input_values_map.at(0), 64 * 128); + std::vector matrix_b = read_float_array_from_binary_file(model_full_path("matmul_64_128_f32.bin")); + ASSERT_EQ(matrix_b.size(), 128 * 128); + std::vector reference_result(64 * 128); + multiply_matrices_and_add_a(matrix_a, matrix_b, reference_result, 64, 128, 128); + + // compare result with the reference + for (size_t i = 0; i < reference_result.size(); ++i) { + EXPECT_NEAR(reference_result[i], result[i], 1e-5); + } +} + +TEST(MLIRExecution, SimpleMatmulf32CLBuffer) { + if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") + GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + + ov::Core core; + auto model = core.read_model( + model_full_path("matmul_64_128_f32.xml")); + + ov::AnyMap device_config; + device_config[ov::hint::performance_mode.name()] = ov::hint::PerformanceMode::THROUGHPUT; + device_config[ov::enable_profiling.name()] = false; + device_config.emplace(ov::hint::inference_precision("f32")); + + auto compiled_model = core.compile_model(model, "GPU", device_config); + + std::map> input_values_map; + input_values_map.emplace(0, std::vector(1, 0.5f)); + + std::vector keep_alive; + + auto input_tensors = allocate_input_tensors(compiled_model, input_values_map, false, keep_alive); + + auto infer_req = compiled_model.create_infer_request(); + for (const auto& input : input_tensors) { + infer_req.set_input_tensor(input.first, input.second); + } + infer_req.infer(); + + auto computed = infer_req.get_output_tensor(0); + float* result = reinterpret_cast(computed.data()); + + // compute reference result + std::vector matrix_a = broadcast_vector(input_values_map.at(0), 64 * 128); + std::vector matrix_b = read_float_array_from_binary_file(model_full_path("matmul_64_128_f32.bin")); + ASSERT_EQ(matrix_b.size(), 128 * 128); + std::vector reference_result(64 * 128); + multiply_matrices_and_add_a(matrix_a, matrix_b, reference_result, 64, 128, 128); + + // compare result with the reference + for (size_t i = 0; i < reference_result.size(); ++i) { + EXPECT_NEAR(reference_result[i], result[i], 1e-5); + } +} + +TEST(MLIRExecution, SimpleMatmulf16) { + if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") + GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + + ov::Core core; + auto model = core.read_model( + model_full_path("matmul_64_128_f16.xml")); + + ov::AnyMap device_config; + device_config[ov::hint::performance_mode.name()] = ov::hint::PerformanceMode::THROUGHPUT; + device_config[ov::enable_profiling.name()] = false; + device_config.emplace(ov::hint::inference_precision("f16")); + + auto compiled_model = core.compile_model(model, "GPU", device_config); + + std::map> input_values_map; + input_values_map.emplace(0, std::vector(1, 0.5f)); + + std::vector keep_alive; + + auto input_tensors = allocate_input_tensors(compiled_model, input_values_map, true, keep_alive); + + auto infer_req = compiled_model.create_infer_request(); + for (const auto& input : input_tensors) { + infer_req.set_input_tensor(input.first, input.second); + } + infer_req.infer(); + + auto computed = infer_req.get_output_tensor(0); + ov::float16* result = reinterpret_cast(computed.data()); + + // compute reference result + std::vector matrix_a = broadcast_vector(input_values_map.at(0), 64 * 128); + std::vector matrix_b = read_float_array_from_binary_file(model_full_path("matmul_64_128_f16.bin"), 2); + ASSERT_EQ(matrix_b.size(), 128 * 128); + std::vector reference_result(64 * 128); + multiply_matrices_and_add_a(matrix_a, matrix_b, reference_result, 64, 128, 128); + + // compare result with the reference + for (size_t i = 0; i < reference_result.size(); ++i) { + EXPECT_NEAR(reference_result[i], result[i], 1e-5); + } +} From 932ed830d75f7e823aa53a9f4932fee7d9cba9ee Mon Sep 17 00:00:00 2001 From: Petr Kurapov Date: Tue, 26 Nov 2024 17:03:08 +0100 Subject: [PATCH 039/121] Fix python execution in GC_GPU mode (#174) * Fixed compatibility with new version of 'wheel' (#25899) - *item1* - *...* - *ticket-id* * Fix python execution in GC_GPU mode. This enables shared libraries build by default in GC to build the CPU runtime and fixes the shape calculation for dynamic cases. --------- Co-authored-by: Ilya Lavrenov --- cmake/graph-compiler.cmake | 7 ++++- src/bindings/python/wheel/CMakeLists.txt | 6 ++-- .../src/transformations/mlir/mlir_op.cpp | 29 +++++++++---------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index a0b3e7268758c1..85942fca9906e4 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -25,9 +25,14 @@ if (NOT DEFINED GRAPH_COMPILER_LIBS) set(GC_ENABLE_LEGACY OFF) set(GC_ENABLE_BINDINGS_PYTHON OFF) set(OV_BUILD_SHARED_LIBS_TMP ${BUILD_SHARED_LIBS}) - set(BUILD_SHARED_LIBS OFF) + set(BUILD_SHARED_LIBS ON) FetchContent_MakeAvailable(GC) set(BUILD_SHARED_LIBS ${OV_BUILD_SHARED_LIBS_TMP}) + FetchContent_GetProperties(GC BINARY_DIR gc_BINARY_DIR) + find_library(GC_CPU_RUNTIME_PATH GcCpuRuntime) + install(FILES ${GC_CPU_RUNTIME_PATH} DESTINATION ${OV_CPACK_RUNTIMEDIR}) + # a hack to not bother with actual file extension + install(FILES ${GC_CPU_RUNTIME_PATH}.20.0git DESTINATION ${OV_CPACK_RUNTIMEDIR}) endif () set(GRAPH_COMPILER_LIBS diff --git a/src/bindings/python/wheel/CMakeLists.txt b/src/bindings/python/wheel/CMakeLists.txt index 733f9e480e4a68..d5f03d2319d5b6 100644 --- a/src/bindings/python/wheel/CMakeLists.txt +++ b/src/bindings/python/wheel/CMakeLists.txt @@ -9,7 +9,7 @@ execute_process(COMMAND ${Python3_EXECUTABLE} -c "from packaging import tags; print(f'{tags.interpreter_name()}{tags.interpreter_version()}')" OUTPUT_VARIABLE PYTHON_TAG OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT PYTHON_TAG) - message(FATAL_ERROR "Failed to detect Python Tag via packaging.tags. Please, check 'packaging' dependency version update") + message(FATAL_ERROR "Failed to detect Python Tag via wheel.vendored.packaging.tags. Please, check 'wheel' dependency version update") endif() execute_process(COMMAND ${Python3_EXECUTABLE} -c "from setuptools.command.bdist_wheel import get_abi_tag; print(f'{get_abi_tag()}')" @@ -18,10 +18,10 @@ if(NOT ABI_TAG) message(FATAL_ERROR "Failed to detect ABI Tag via setuptools.command.bdist_wheel. Please, check 'setuptools' dependency version update") endif() -execute_process(COMMAND ${Python3_EXECUTABLE} -c "from packaging import tags; print(f'{next(tags.platform_tags())}')" +execute_process(COMMAND ${Python3_EXECUTABLE} -c "import wheel.vendored.packaging.tags as tags ; print(f'{next(tags.platform_tags())}')" OUTPUT_VARIABLE PLATFORM_TAG OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT PLATFORM_TAG) - message(FATAL_ERROR "Failed to detect Platform Tag via packaging.tags. Please, check 'packaging' dependency version update") + message(FATAL_ERROR "Failed to detect Platform Tag via wheel.vendored.packaging.tags. Please, check 'wheel' dependency version update") endif() # defines wheel architecture part of `PLATFORM_TAG` diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 8ab1dd8fb25830..e296ff263cdccb 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -5,6 +5,7 @@ #include "mlir_op.hpp" #include +#include #include #include #include @@ -232,24 +233,22 @@ std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext struct MemRefDescriptor { MemRefDescriptor() = default; - MemRefDescriptor (ov::Tensor tensor, const ov::Shape& module_input_shape) + MemRefDescriptor (ov::Tensor tensor, const ov::PartialShape& module_input_shape) : allocated(tensor.data()), aligned(tensor.data()), - offset(0), - shape(module_input_shape.begin(), module_input_shape.end()) { - if (shape.size() != tensor.get_shape().size()) { - // validate that the shape difference is due to trailing '1's - for (size_t i = 0; i < shape.size(); ++i) { - if (shape[i] != tensor.get_shape()[i]) { - OPENVINO_THROW("Mismatch in shape sizes"); - } - } - for (size_t i = shape.size(); i < tensor.get_shape().size(); ++i) { - if (tensor.get_shape()[i] != 1) { - OPENVINO_THROW("Mismatch in shape sizes"); - } + offset(0) { + if (module_input_shape.rank() == shape_size(tensor.get_shape())) { + shape.assign(tensor.get_shape().begin(), tensor.get_shape().end()); + } else { + auto it = tensor.get_shape().begin(); + std::advance(it, module_input_shape.rank().get_length()); + shape.assign(tensor.get_shape().begin(), it); + + if (std::any_of(it, tensor.get_shape().end(), [](size_t dim) {return dim != 1;})) { + OPENVINO_THROW("Mismatch in shape sizes"); } } + strides.resize(shape.size()); const auto& byte_strides = tensor.get_strides(); auto element_size = tensor.get_element_type().size(); @@ -511,7 +510,7 @@ bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs, std::vector memref_args; for (size_t i = 0; i < inputs.size(); ++i) { - auto& initial_shape = get_input_shape(i); + auto& initial_shape = get_input_partial_shape(i); memref_args.push_back(MemRefDescriptor(inputs[i], initial_shape)); } for (size_t i = 0; i < outputs.size(); ++i) { From a7fe8440fa7fc8b88846953a628bcca53b708be1 Mon Sep 17 00:00:00 2001 From: Petr Kurapov Date: Tue, 17 Dec 2024 15:07:12 +0100 Subject: [PATCH 040/121] Add floor, shape_of and squeeze patterns (#175) * Add floor and squeeze patterns * Add shape_of conversion --- .../src/transformations/mlir/convert.cpp | 7 +++ .../transformations/mlir/convert_common.cpp | 21 +++---- .../src/transformations/mlir/mlir_op.cpp | 3 + .../src/transformations/mlir/op/floor.cpp | 46 +++++++++++++++ .../src/transformations/mlir/op/floor.hpp | 23 ++++++++ .../src/transformations/mlir/op/shape_of.cpp | 46 +++++++++++++++ .../src/transformations/mlir/op/shape_of.hpp | 23 ++++++++ .../src/transformations/mlir/op/squeeze.cpp | 56 +++++++++++++++++++ .../src/transformations/mlir/op/squeeze.hpp | 23 ++++++++ 9 files changed, 236 insertions(+), 12 deletions(-) create mode 100644 src/common/transformations/src/transformations/mlir/op/floor.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/floor.hpp create mode 100644 src/common/transformations/src/transformations/mlir/op/shape_of.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/shape_of.hpp create mode 100644 src/common/transformations/src/transformations/mlir/op/squeeze.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/squeeze.hpp diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index 8372cc86c4cda8..7729124ea1ceec 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -74,6 +74,9 @@ #include "mlir_op.hpp" #include "op/matmul.hpp" #include "op/relu.hpp" +#include "op/floor.hpp" +#include "op/shape_of.hpp" +#include "op/squeeze.hpp" #include "op/binary_eltwise.hpp" #include "openvino/core/dimension.hpp" #include "openvino/core/rt_info.hpp" @@ -311,6 +314,9 @@ void injectMLIR(std::shared_ptr model, manager.register_pass>(); manager.register_pass>(); manager.register_pass(); + manager.register_pass(); + manager.register_pass(); + manager.register_pass(); manager.register_pass(); manager.register_pass(context, mode, loweringContext); manager.run_passes(model); @@ -322,6 +328,7 @@ void loadDialects(MLIRContext* context) { context->loadDialect(); context->loadDialect(); context->loadDialect(); + context->loadDialect(); } MLIRContext* get_shared_mlir_context(MlirMode mode) { diff --git a/src/common/transformations/src/transformations/mlir/convert_common.cpp b/src/common/transformations/src/transformations/mlir/convert_common.cpp index dee142a554fe79..e35e3d5dd2366f 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.cpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.cpp @@ -78,6 +78,9 @@ Location createLayerLocation(MLIRContext* ctx, const std::string& layerName, con } SmallVector importShape(const ov::PartialShape& shape) { + if (shape.rank().is_dynamic()) { + OPENVINO_THROW("Dynamic ranks are not supported."); + } SmallVector out(shape.rank().get_length()); // TODO: Add support for dynamically ranked shapes for (size_t i = 0; i < out.size(); ++i) { @@ -98,27 +101,21 @@ Type importPrecision(MLIRContext* ctx, const ov::element::Type& precision) { case ov::element::Type_t::bf16: return BFloat16Type::get(ctx); case ov::element::Type_t::i64: - return getSInt64Type(ctx); case ov::element::Type_t::u64: - return getUInt64Type(ctx); + return IntegerType::get(ctx, 64); case ov::element::Type_t::i32: - return getSInt32Type(ctx); case ov::element::Type_t::u32: - return getUInt32Type(ctx); + return IntegerType::get(ctx, 32); case ov::element::Type_t::i16: - return getSInt16Type(ctx); case ov::element::Type_t::u16: - return getUInt16Type(ctx); + return IntegerType::get(ctx, 16); case ov::element::Type_t::i8: - return getSInt8Type(ctx); case ov::element::Type_t::u8: - return getUInt8Type(ctx); + case ov::element::Type_t::boolean: + return IntegerType::get(ctx, 8); case ov::element::Type_t::i4: - return getSInt4Type(ctx); case ov::element::Type_t::u4: - return getUInt4Type(ctx); - case ov::element::Type_t::boolean: - return getBool8Type(ctx); + return IntegerType::get(ctx, 4); default: OPENVINO_THROW("Unsupported element_type: ", precision); } diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index e296ff263cdccb..6351a9f310ca91 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -101,6 +101,9 @@ void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module, pm.addNestedPass(createCanonicalizerPass()); pm.addNestedPass(createCSEPass()); + // Rewrite shape ops in tensor/arith/etc + pm.addPass(createConvertShapeToStandardPass()); + // Remove empty tensors to avoid converting them into temporary buffers. pm.addPass(bufferization::createEmptyTensorEliminationPass()); diff --git a/src/common/transformations/src/transformations/mlir/op/floor.cpp b/src/common/transformations/src/transformations/mlir/op/floor.cpp new file mode 100644 index 00000000000000..32758fe8f564b6 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/floor.cpp @@ -0,0 +1,46 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" + +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "floor.hpp" +#include "../convert_common.hpp" + + +namespace { + +using namespace ov::mlir; + +struct ConvertFloor { + void operator()(ConversionContext& context, NodePtr node) { + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto input = context.getInputs(node)[0]; + const auto ov_output_element_type = node->get_output_element_type(0); + const auto ov_output_shape = node->get_output_partial_shape(0); + auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); + auto dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); + auto empty = builder.create(loc, outType, dynamic_dimensions); + auto floor = builder.create(loc, mlir::ValueRange{input}, mlir::ValueRange{empty}); + context.addOutputs(node, floor); + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +FloorPattern::FloorPattern() + : MarkPattern(wrap_type({any_input()}), ConvertFloor()) {} + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/floor.hpp b/src/common/transformations/src/transformations/mlir/op/floor.hpp new file mode 100644 index 00000000000000..860818cb727a06 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/floor.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class FloorPattern : public MarkPattern { +public: + OPENVINO_RTTI("FloorPattern", "0"); + FloorPattern(); +}; + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/shape_of.cpp b/src/common/transformations/src/transformations/mlir/op/shape_of.cpp new file mode 100644 index 00000000000000..9b27e3f70a4a41 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/shape_of.cpp @@ -0,0 +1,46 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/Dialect/Shape/IR/Shape.h" +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/Dialect/Arith/IR/Arith.h" + +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "shape_of.hpp" +#include "../convert_common.hpp" + + +namespace { + +using namespace ov::mlir; + +struct ConvertShapeOf { + void operator()(ConversionContext& context, NodePtr node) { + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto ov_output_element_type = node->get_output_element_type(0); + const auto ov_output_shape = node->get_output_partial_shape(0); + const auto input = context.getInputs(node)[0]; + auto shapeOf = builder.create(loc, mlir::ValueRange{input}); + auto casted_type = RankedTensorType::get(ArrayRef(importShape(ov_output_shape)), importPrecision(context.context, ov_output_element_type)); + auto cast = builder.create(loc, casted_type, mlir::ValueRange{shapeOf}); + context.addOutputs(node, cast); + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +ShapeOfPattern::ShapeOfPattern() : MarkPattern(wrap_type({any_input()}), ConvertShapeOf()) {} + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/shape_of.hpp b/src/common/transformations/src/transformations/mlir/op/shape_of.hpp new file mode 100644 index 00000000000000..1915004057695f --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/shape_of.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class ShapeOfPattern : public MarkPattern { +public: + OPENVINO_RTTI("ShapeOfPattern", "0"); + ShapeOfPattern(); +}; + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/squeeze.cpp b/src/common/transformations/src/transformations/mlir/op/squeeze.cpp new file mode 100644 index 00000000000000..025fb9b51d0acc --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/squeeze.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" + +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "squeeze.hpp" +#include "../convert_common.hpp" + + +namespace { + +using namespace ov::mlir; + +struct ConvertSqueeze { + void operator()(ConversionContext& context, NodePtr node) { + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto input = context.getInputs(node)[0]; + + auto src_partial_shape = node->get_input_partial_shape(0); + auto src_rank = src_partial_shape.rank().get_length(); + SmallVector collapse_groups; + ReassociationIndices group = ReassociationIndices(); + for (size_t src_i = 0; src_i < src_rank; src_i++) { + auto src_d = src_partial_shape[src_i]; + group.push_back(src_i); + if (src_d.is_static() && src_d.get_length() == 1) { + // continue collecting + } else { + collapse_groups.emplace_back(group); + group = ReassociationIndices(); + } + } + + auto reshape = builder.create(loc, input, collapse_groups); + context.addOutputs(node, reshape); + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +SqueezePattern::SqueezePattern() : MarkPattern(wrap_type({any_input()}), ConvertSqueeze()) {} + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/squeeze.hpp b/src/common/transformations/src/transformations/mlir/op/squeeze.hpp new file mode 100644 index 00000000000000..57d064e4c112ea --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/squeeze.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class SqueezePattern : public MarkPattern { +public: + OPENVINO_RTTI("SqueezePattern", "0"); + SqueezePattern(); +}; + +} // namespace mlir +} // namespace ov From cc4d614904de2b5d5e198301c5a215f3c0e822a3 Mon Sep 17 00:00:00 2001 From: Petr Kurapov Date: Tue, 17 Dec 2024 16:39:32 +0100 Subject: [PATCH 041/121] Add gather, slice and concat patterns (#177) * Add floor and squeeze patterns * Add shape_of conversion * fixup! Add shape_of conversion * Add gather pattern * Add slice pattern * Enable slice and gather patterns * Add concat pattern --------- Co-authored-by: Sergey Lyalin --- .../src/transformations/mlir/convert.cpp | 8 ++ .../src/transformations/mlir/op/concat.cpp | 52 +++++++++++ .../src/transformations/mlir/op/concat.hpp | 23 +++++ .../src/transformations/mlir/op/gather.cpp | 87 +++++++++++++++++++ .../src/transformations/mlir/op/gather.hpp | 24 +++++ .../src/transformations/mlir/op/slice.cpp | 53 +++++++++++ .../src/transformations/mlir/op/slice.hpp | 23 +++++ 7 files changed, 270 insertions(+) create mode 100644 src/common/transformations/src/transformations/mlir/op/concat.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/concat.hpp create mode 100644 src/common/transformations/src/transformations/mlir/op/gather.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/gather.hpp create mode 100644 src/common/transformations/src/transformations/mlir/op/slice.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/slice.hpp diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index 7729124ea1ceec..d3a1acbd029a14 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -72,10 +72,13 @@ #endif #include "mlir_op.hpp" +#include "op/concat.hpp" #include "op/matmul.hpp" #include "op/relu.hpp" #include "op/floor.hpp" +#include "op/gather.hpp" #include "op/shape_of.hpp" +#include "op/slice.hpp" #include "op/squeeze.hpp" #include "op/binary_eltwise.hpp" #include "openvino/core/dimension.hpp" @@ -247,6 +250,8 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, dm.reserve(shape.size()); for (size_t j = 0; j < shape.size(); ++j) { auto dim = shape[j]; + if (dim.is_dynamic()) + assert(input_map.count(ov::symbol::ancestor_of(dim.get_symbol())) && "Input map is missing a symbol for dynamic dim"); dm.push_back(dim.is_dynamic() ? input_map.at(ov::symbol::ancestor_of(dim.get_symbol())) : empty); } output_map.emplace_back(dm); @@ -313,9 +318,12 @@ void injectMLIR(std::shared_ptr model, manager.register_pass>(); manager.register_pass>(); manager.register_pass>(); + manager.register_pass(); manager.register_pass(); manager.register_pass(); + manager.register_pass(); manager.register_pass(); + manager.register_pass(); manager.register_pass(); manager.register_pass(); manager.register_pass(context, mode, loweringContext); diff --git a/src/common/transformations/src/transformations/mlir/op/concat.cpp b/src/common/transformations/src/transformations/mlir/op/concat.cpp new file mode 100644 index 00000000000000..951ecbb23b8385 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/concat.cpp @@ -0,0 +1,52 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" + +#include +#include "openvino/opsets/opset1.hpp" +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "concat.hpp" +#include "../convert_common.hpp" + +namespace { + +using namespace ov::mlir; + +struct ConvertConcat { + void operator()(ConversionContext& context, NodePtr node) { + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto inputs = context.getInputs(node); + + const auto ov_element_type = node->get_input_element_type(0); + const auto src_partial_shape = node->get_input_partial_shape(0); + const auto rank = src_partial_shape.rank().get_length(); + + auto concat_node = std::dynamic_pointer_cast(node); + int64_t axis = concat_node->get_axis(); + if (axis < 0) { + axis += rank; + } + + auto concat = builder.create(loc, axis, mlir::ValueRange{inputs}); + context.addOutputs(node, concat); + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +ConcatPattern::ConcatPattern() : MarkPattern(wrap_type(), ConvertConcat()) {} + +} // namespace mlir +} // namespace ov + diff --git a/src/common/transformations/src/transformations/mlir/op/concat.hpp b/src/common/transformations/src/transformations/mlir/op/concat.hpp new file mode 100644 index 00000000000000..bd1dfece8634f6 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/concat.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class ConcatPattern : public MarkPattern { +public: + OPENVINO_RTTI("ConcatPattern", "0"); + ConcatPattern(); +}; + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/gather.cpp b/src/common/transformations/src/transformations/mlir/op/gather.cpp new file mode 100644 index 00000000000000..229f24380e5ef2 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/gather.cpp @@ -0,0 +1,87 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/Dialect/Shape/IR/Shape.h" +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/Dialect/Arith/IR/Arith.h" + +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "gather.hpp" +#include "../convert_common.hpp" + +namespace { + +using namespace ov::mlir; + +struct ConvertGather { + void operator()(ConversionContext& context, NodePtr node) { + // TODO: support batch attribute + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto input = context.getInputs(node)[0]; + const auto indices = context.getInputs(node)[1]; + // get_axis() seems to be enough? + // const auto axis = context.getInputs(node)[2]; + + const auto ov_index_element_type = node->get_input_element_type(1); + const auto ov_index_shape = node->get_input_partial_shape(1); + auto dynamic_index_dims = context.get_dynamic_dimension_values(ov_index_shape); + const auto ov_output_element_type = node->get_output_element_type(0); + const auto ov_output_shape = node->get_output_partial_shape(0); + auto out_type = importTensor(context.context, ov_output_shape, ov_output_element_type); + RankedTensorType indices_type = importTensor(context.context, ov_index_shape, ov_index_element_type); + + bool is_input_scalar = ov_index_shape.rank().is_static() && ov_index_shape.rank().get_length() == 0; + + // `shape_of` returns tensor<1xindex> for scalars, gather requires dimention match at `gather_dims` indices. + // This expands the shape of input indices to be <1xi64> in case of scalars to resolve types mismatch. + Value indices_expanded = indices; + if (is_input_scalar) { + SmallVector new_shape({1}); + indices_type = RankedTensorType::get(new_shape, importPrecision(context.context, ov_index_element_type)); + SmallVector reassociation; // intentionally empty for scalar + auto expanded = builder.create(loc, indices_type, indices, reassociation); + indices_expanded = expanded.getResult(); + } + + // Convert negative indices into positive ones: compare to zero and select from orinal or a sum based on + // the resulting predicate. + auto empty = builder.create(loc, indices_type, dynamic_index_dims); + auto zero = getConstant(builder, ov_index_element_type, 0); + auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); + auto pred = arith::CmpIPredicate::slt; + auto cmpi = builder.create(loc, pred, indices_expanded, fill.getResult(0)); + auto shape_of = builder.create(loc, mlir::ValueRange{input}); + auto cast = builder.create(loc, indices_type, mlir::ValueRange{shape_of}); + + auto empty_add = builder.create(loc, indices_expanded.getType(), dynamic_index_dims); + auto add = builder.create(loc, mlir::ValueRange{cast.getResult(), indices_expanded}, mlir::ValueRange{empty_add}); + auto select = builder.create(loc, mlir::ValueRange{cmpi.getResult(), add.getResult(0), indices_expanded}, mlir::ValueRange{empty_add}); + + auto gather_node = std::dynamic_pointer_cast(node); + assert(gather_node && "Expected a gather node"); + int64_t axis = gather_node->get_axis(); + + llvm::SmallVector gather_dims{axis}; + auto gather = builder.create(loc, out_type, input, select.getResult(0), gather_dims, false); + context.addOutputs(node, gather); + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +GatherPattern::GatherPattern() : MarkPattern(wrap_type({any_input(), any_input(), any_input()}), ConvertGather()) {} + +} // namespace mlir +} // namespace ov + diff --git a/src/common/transformations/src/transformations/mlir/op/gather.hpp b/src/common/transformations/src/transformations/mlir/op/gather.hpp new file mode 100644 index 00000000000000..8746136730526d --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/gather.hpp @@ -0,0 +1,24 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class GatherPattern : public MarkPattern { +public: + OPENVINO_RTTI("GatherPattern", "0"); + GatherPattern(); +}; + +} // namespace mlir +} // namespace ov + diff --git a/src/common/transformations/src/transformations/mlir/op/slice.cpp b/src/common/transformations/src/transformations/mlir/op/slice.cpp new file mode 100644 index 00000000000000..49b3cc64a12e93 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/slice.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" + +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "slice.hpp" +#include "../convert_common.hpp" + +namespace { + +using namespace ov::mlir; + +struct ConvertSlice { + void operator()(ConversionContext& context, NodePtr node) { + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto input = context.getInputs(node)[0]; + const auto start = context.getInputs(node)[1]; + const auto stop = context.getInputs(node)[2]; + const auto step = context.getInputs(node)[3]; + const auto axes = context.getInputs(node)[4]; + + const auto ov_index_shape = node->get_input_partial_shape(1); + const auto ov_index_element_type = node->get_input_element_type(1); + auto dynamic_index_dims = context.get_dynamic_dimension_values(ov_index_shape); + + auto index_type = importTensor(context.context, ov_index_shape, ov_index_element_type); + auto empty = builder.create(loc, index_type, dynamic_index_dims); + + // TODO: this only works for the all-positive numbers case. + auto sizes = builder.create(loc, mlir::ValueRange{stop, start}, mlir::ValueRange{empty}); + auto slice = builder.create(loc, input, mlir::ValueRange{start}, mlir::ValueRange{sizes.getResults()}, mlir::ValueRange{step}); + context.addOutputs(node, slice); + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +SlicePattern::SlicePattern() : MarkPattern(wrap_type({any_input(), any_input(), any_input(), any_input(), any_input()}), ConvertSlice()) {} + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/slice.hpp b/src/common/transformations/src/transformations/mlir/op/slice.hpp new file mode 100644 index 00000000000000..b4ba5a9cdca645 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/slice.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class SlicePattern : public MarkPattern { +public: + OPENVINO_RTTI("SlicePattern", "0"); + SlicePattern(); +}; + +} // namespace mlir +} // namespace ov From af561ef13630f07f83333215e8fb958a56751974 Mon Sep 17 00:00:00 2001 From: Petr Kurapov Date: Wed, 18 Dec 2024 12:41:19 +0100 Subject: [PATCH 042/121] Add unsqueeze and transpose patterns (#178) * Add floor and squeeze patterns * Add shape_of conversion * fixup! Add shape_of conversion * Add gather pattern * Add slice pattern * Enable slice and gather patterns * Add concat pattern * Add unsqueeze pattern * Add transpose pattern * fixup! Add transpose pattern --------- Co-authored-by: Sergey Lyalin --- .../src/transformations/mlir/convert.cpp | 4 + .../src/transformations/mlir/op/transpose.cpp | 54 +++++++++++++ .../src/transformations/mlir/op/transpose.hpp | 23 ++++++ .../src/transformations/mlir/op/unsqueeze.cpp | 75 +++++++++++++++++++ .../src/transformations/mlir/op/unsqueeze.hpp | 24 ++++++ 5 files changed, 180 insertions(+) create mode 100644 src/common/transformations/src/transformations/mlir/op/transpose.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/transpose.hpp create mode 100644 src/common/transformations/src/transformations/mlir/op/unsqueeze.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/unsqueeze.hpp diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index d3a1acbd029a14..eec817066c09e1 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -80,6 +80,8 @@ #include "op/shape_of.hpp" #include "op/slice.hpp" #include "op/squeeze.hpp" +#include "op/transpose.hpp" +#include "op/unsqueeze.hpp" #include "op/binary_eltwise.hpp" #include "openvino/core/dimension.hpp" #include "openvino/core/rt_info.hpp" @@ -325,6 +327,8 @@ void injectMLIR(std::shared_ptr model, manager.register_pass(); manager.register_pass(); manager.register_pass(); + manager.register_pass(); + manager.register_pass(); manager.register_pass(); manager.register_pass(context, mode, loweringContext); manager.run_passes(model); diff --git a/src/common/transformations/src/transformations/mlir/op/transpose.cpp b/src/common/transformations/src/transformations/mlir/op/transpose.cpp new file mode 100644 index 00000000000000..aa08258e131b4b --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/transpose.cpp @@ -0,0 +1,54 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" + +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "transpose.hpp" +#include "../convert_common.hpp" + +namespace { + +using namespace ov::mlir; + +struct ConvertTranspose { + void operator()(ConversionContext& context, NodePtr node) { + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto input = context.getInputs(node)[0]; + // TODO: support dynamic inputs + // const auto order = context.getInputs(node)[1]; + + const auto ov_output_element_type = node->get_output_element_type(0); + const auto ov_output_shape = node->get_output_partial_shape(0); + auto out_type = importTensor(context.context, ov_output_shape, ov_output_element_type); + auto dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); + + auto const_order = dynamic_cast(node->get_input_node_ptr(1)); + assert(const_order && "non-const order not supported"); + ov::Coordinate coords = const_order->get_coordinate_val(); + SmallVector order(coords.begin(), coords.end()); + + auto empty = builder.create(loc, out_type, dynamic_dimensions); + auto transpose = builder.create(loc, input, empty, order); + context.addOutputs(node, transpose); + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +TransposePattern::TransposePattern() : MarkPattern(wrap_type({any_input(), any_input()}), ConvertTranspose()) {} + +} // namespace mlir +} // namespace ov + diff --git a/src/common/transformations/src/transformations/mlir/op/transpose.hpp b/src/common/transformations/src/transformations/mlir/op/transpose.hpp new file mode 100644 index 00000000000000..22de4ddf0b4935 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/transpose.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class TransposePattern : public MarkPattern { +public: + OPENVINO_RTTI("TransposePattern", "0"); + TransposePattern(); +}; + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/unsqueeze.cpp b/src/common/transformations/src/transformations/mlir/op/unsqueeze.cpp new file mode 100644 index 00000000000000..2e82195d2d912f --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/unsqueeze.cpp @@ -0,0 +1,75 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/Dialect/Shape/IR/Shape.h" +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" + +#include +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "unsqueeze.hpp" +#include "../convert_common.hpp" + + +namespace { + +using namespace ov::mlir; + +struct ConvertUnsqueeze { + void operator()(ConversionContext& context, NodePtr node) { + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto input = context.getInputs(node)[0]; + // TODO: support dynamic inputs + // const auto axes = context.getInputs(node)[1]; + + const auto ov_output_element_type = node->get_output_element_type(0); + const auto ov_input_shape = node->get_input_partial_shape(0); + + assert(ov_input_shape.rank().is_static() && "expecting static output shape"); + + auto const_axes = dynamic_cast(node->get_input_node_ptr(1)); + assert(const_axes && "non-const axes not supported"); + ov::Coordinate coords = const_axes->get_coordinate_val(); + + // Calculate the resulting shape. + // E.g., for an input tensor<4x2xf32> and axes [0, 2] (tensor<2xi64>) need to build a shape 1x4x1x2 + SmallVector expand_groups; + ReassociationIndices group = ReassociationIndices(); + SmallVector shape(coords.size() + ov_input_shape.rank().get_length()); + for (size_t input_idx = 0, coord_idx = 0, i = 0; i < shape.size(); ++i) { + group.push_back(i); + if (coord_idx < coords.size() && i == coords[coord_idx]) { + shape[i] = 1; + coord_idx++; + } else { + const auto& dim = ov_input_shape[input_idx]; + shape[i] = dim.is_dynamic() ? ShapedType::kDynamic : dim.get_length(); + input_idx++; + expand_groups.push_back(group); + group = ReassociationIndices(); + } + } + + auto result_type = RankedTensorType::get(shape, importPrecision(context.context, ov_output_element_type)); + auto expand_shape = builder.create(loc, result_type, input, expand_groups); + context.addOutputs(node, expand_shape); + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +UnsqueezePattern::UnsqueezePattern() : MarkPattern(wrap_type({any_input(), any_input()}), ConvertUnsqueeze()) {} + +} // namespace mlir +} // namespace ov + diff --git a/src/common/transformations/src/transformations/mlir/op/unsqueeze.hpp b/src/common/transformations/src/transformations/mlir/op/unsqueeze.hpp new file mode 100644 index 00000000000000..21d4eb44b53b52 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/unsqueeze.hpp @@ -0,0 +1,24 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class UnsqueezePattern : public MarkPattern { +public: + OPENVINO_RTTI("UnsqueezePattern", "0"); + UnsqueezePattern(); +}; + +} // namespace mlir +} // namespace ov + From b05ab459d16d7ae7ed34ed26b7df84e2307d2f8a Mon Sep 17 00:00:00 2001 From: zhicong zhong Date: Wed, 18 Dec 2024 20:37:01 +0800 Subject: [PATCH 043/121] Fix python e2e buld (#179) * fix build * support build without pre-built llvm * Fix packaging * remove unused changes * clean up code --------- Co-authored-by: Petr Kurapov --- cmake/graph-compiler.cmake | 27 +++++++++++++++++++++++---- setup.py | 9 +++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index 85942fca9906e4..01dae4cb144aeb 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -25,14 +25,33 @@ if (NOT DEFINED GRAPH_COMPILER_LIBS) set(GC_ENABLE_LEGACY OFF) set(GC_ENABLE_BINDINGS_PYTHON OFF) set(OV_BUILD_SHARED_LIBS_TMP ${BUILD_SHARED_LIBS}) - set(BUILD_SHARED_LIBS ON) + set(BUILD_SHARED_LIBS OFF) + + FetchContent_GetProperties(GC) + if(NOT GC_POPULATED) + FetchContent_Populate(GC) + endif() + if(NOT DEFINED LLVM_DIR OR NOT DEFINED MLIR_DIR) + execute_process( + COMMAND /bin/bash ./scripts/compile.sh --dev --llvm --imex + WORKING_DIRECTORY ${gc_SOURCE_DIR} + ) + + SET(LLVM_INST_PATH "${gc_SOURCE_DIR}/externals/llvm-project/build") + SET(LLVM_DIR "${LLVM_INST_PATH}/lib/cmake/llvm") + SET(MLIR_DIR "${LLVM_INST_PATH}/lib/cmake/mlir") + add_definitions(-DMLIR_RUNNER_UTILS_PATH=${LLVM_INST_PATH}/lib) + add_subdirectory(${gc_SOURCE_DIR} ${gc_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() + FetchContent_MakeAvailable(GC) set(BUILD_SHARED_LIBS ${OV_BUILD_SHARED_LIBS_TMP}) FetchContent_GetProperties(GC BINARY_DIR gc_BINARY_DIR) - find_library(GC_CPU_RUNTIME_PATH GcCpuRuntime) - install(FILES ${GC_CPU_RUNTIME_PATH} DESTINATION ${OV_CPACK_RUNTIMEDIR}) + ov_cpack_add_component(GcCpuRuntime HIDDEN) + set(GC_CPU_RUNTIME_PATH ${gc_BINARY_DIR}/lib/libGcCpuRuntime.so) + install(FILES ${GC_CPU_RUNTIME_PATH} DESTINATION ${OV_CPACK_RUNTIMEDIR} COMPONENT GcCpuRuntime) # a hack to not bother with actual file extension - install(FILES ${GC_CPU_RUNTIME_PATH}.20.0git DESTINATION ${OV_CPACK_RUNTIMEDIR}) + install(FILES ${GC_CPU_RUNTIME_PATH}.20.0git DESTINATION ${OV_CPACK_RUNTIMEDIR} COMPONENT GcCpuRuntime) endif () set(GRAPH_COMPILER_LIBS diff --git a/setup.py b/setup.py index 631fcebd237f01..a3998dc6939dfc 100644 --- a/setup.py +++ b/setup.py @@ -129,6 +129,13 @@ "prefix": f"{BUILD_BASE}/libs.intel_omp", "install_dir": OV_RUNTIME_LIBS_DIR, "binary_dir": OPENVINO_BINARY_DIR, + } + "gc_libs": { + "name": "GcCpuRuntime", + "prefix": f"{BUILD_BASE}/libs.gc", + "install_dir": OV_RUNTIME_LIBS_DIR, + "rpath": LIBS_RPATH, + "binary_dir": OPENVINO_BINARY_DIR, }, "pugixml_libs": { "name": "pugixml", @@ -437,6 +444,8 @@ def resolve_symlinks(self, local_base_dir: Path): for real_name, symlink in file_dict.items(): os.unlink(symlink) os.rename(real_name, symlink) + if "libs.gc" not in str(local_base_dir): + os.rename(real_name, symlink) self.announce(f"Resolved symlink {symlink} as {real_name}", level=log.INFO) def copy_package_libs(self, src_dirs): From 27ae57d915c7a615fcf23e492947b62c8a00859f Mon Sep 17 00:00:00 2001 From: dchigarev Date: Thu, 29 Jan 2026 13:04:34 +0000 Subject: [PATCH 044/121] Make it work with new llvm & ov Signed-off-by: dchigarev --- setup.py | 2 +- src/cmake/openvino.cmake | 2 +- .../src/transformations/mlir/convert.cpp | 8 +++-- .../src/transformations/mlir/mlir_op.cpp | 18 ++++++---- .../intel_gpu/src/graph/generic_primitive.cpp | 2 +- .../graph/impls/common/generic_primitive.cpp | 12 +++++-- .../graph/impls/common/generic_primitive.hpp | 36 +++++++++++++++++++ .../graph/include/generic_primitive_inst.h | 2 ++ .../registry/generic_primitive_impls.cpp | 27 ++++++++++++++ .../intel_gpu/src/graph/registry/registry.hpp | 1 + .../tests/functional/mlir_op/sanity_tests.cpp | 17 ++++----- 11 files changed, 105 insertions(+), 22 deletions(-) create mode 100644 src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.hpp create mode 100644 src/plugins/intel_gpu/src/graph/registry/generic_primitive_impls.cpp diff --git a/setup.py b/setup.py index a3998dc6939dfc..9e3afcc7b004e9 100644 --- a/setup.py +++ b/setup.py @@ -129,7 +129,7 @@ "prefix": f"{BUILD_BASE}/libs.intel_omp", "install_dir": OV_RUNTIME_LIBS_DIR, "binary_dir": OPENVINO_BINARY_DIR, - } + }, "gc_libs": { "name": "GcCpuRuntime", "prefix": f"{BUILD_BASE}/libs.gc", diff --git a/src/cmake/openvino.cmake b/src/cmake/openvino.cmake index 43a64b71e4f9bd..a89e228293b8c8 100644 --- a/src/cmake/openvino.cmake +++ b/src/cmake/openvino.cmake @@ -59,7 +59,7 @@ target_link_libraries(${TARGET_NAME} openvino::pugixml ${CMAKE_DL_LIBS} ${GRAPH_COMPILER_LIBS} - ${MLIR_OPENVINO_LIBS} + ${MLIR_ALL_LIBS} Threads::Threads PUBLIC $<$,$,9.1>>:stdc++fs> $<$,$,9.0>>:c++fs>) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index eec817066c09e1..9011a3ffaaaeff 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -38,6 +38,7 @@ #include "mlir/Dialect/Linalg/TransformOps/DialectExtension.h" #include "mlir/Dialect/MemRef/IR/MemRef.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/Dialect/Shape/IR/Shape.h" #include "mlir/Dialect/Tensor/TransformOps/TensorTransformOps.h" #include "mlir/Dialect/Vector/IR/VectorOps.h" #include "mlir/ExecutionEngine/ExecutionEngine.h" @@ -149,7 +150,7 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, DataLayoutEntryInterface entry = DataLayoutEntryAttr::get(context, key, tileSize); TargetDeviceSpecInterface deviceSpec = TargetDeviceSpecAttr::get(context, ArrayRef(entry)); auto deviceStr = StringAttr::get(context, "CPU"); - auto sysSpec = TargetSystemSpecAttr::get(context, ArrayRef(std::pair(deviceStr, deviceSpec))); + auto sysSpec = TargetSystemSpecAttr::get(context, {DataLayoutEntryAttr::get(deviceStr, deviceSpec)}); module.getOperation()->setAttr("#dlti.sys_spec", sysSpec); ConversionContext conversion_context(context, &block_builder); @@ -158,7 +159,10 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, auto funcInputVal = func.getArgument(i); // transition from memref enclosure to tensor interior auto loc = createLocation(context, inputs[i].get_node_shared_ptr()); - auto tensor = block_builder.create(loc, funcInputVal, /*restrict = */ true); + auto ranked = mlir::dyn_cast(funcInputVal.getType()); + auto tensorTy = mlir::RankedTensorType::get(ranked.getShape(), ranked.getElementType()); + auto tensor = block_builder.create( + loc, tensorTy, funcInputVal, /*restrict = */ true, /*writable=*/ true); conversion_context.nodeOutputMap.emplace(inputs[i], tensor); // FIXME: Avoid pre-population of dimension_map, take dimension values only if needed diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 6351a9f310ca91..3e5e409cb00f33 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -25,6 +25,9 @@ #include "llvm/Target/TargetOptions.h" #include "mlir/Dialect/Arith/IR/Arith.h" #include "mlir/Dialect/Arith/Transforms/Passes.h" +#include "mlir/Transforms/Passes.h" +#include "mlir/Conversion/Passes.h" +#include "mlir/Dialect/MemRef/Transforms/Passes.h" #include "mlir/Dialect/Bufferization/Transforms/Passes.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" @@ -108,16 +111,17 @@ void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module, pm.addPass(bufferization::createEmptyTensorEliminationPass()); pm.addPass(bufferization::createOneShotBufferizePass()); - pm.addNestedPass(bufferization::createFinalizingBufferizePass()); // Cleanup after bufferization - possibly remove redundant copies. pm.addNestedPass(createCanonicalizerPass()); pm.addNestedPass(createCSEPass()); // Deallocation pipeline to avoid memory leaks from created temporary buffers. - pm.addPass(memref::createExpandReallocPass(/*emitDeallocs=*/false)); + memref::ExpandReallocPassOptions expandReallocOpts; + expandReallocOpts.emitDeallocs = false; + pm.addPass(memref::createExpandReallocPass(expandReallocOpts)); pm.addPass(createCanonicalizerPass()); - bufferization::DeallocationOptions deallocOpts; + bufferization::OwnershipBasedBufferDeallocationPassOptions deallocOpts; deallocOpts.privateFuncDynamicOwnership = false; pm.addPass(bufferization::createOwnershipBasedBufferDeallocationPass(deallocOpts)); pm.addPass(createCanonicalizerPass()); @@ -134,10 +138,11 @@ void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module, // Blanket-convert any remaining affine ops if any remain. pm.addPass(createLowerAffinePass()); // Convert SCF to CF (always needed). - pm.addPass(createConvertSCFToCFPass()); + pm.addPass(createSCFToControlFlowPass()); // Sprinkle some cleanups. pm.addPass(createCanonicalizerPass()); pm.addPass(createCSEPass()); + pm.addPass(createArithToLLVMConversionPass()); // Blanket-convert any remaining linalg ops to LLVM if any remain. // pm.addPass(createConvertLinalgToLLVMPass()); // no such pass // Convert vector to LLVM (always needed). @@ -194,9 +199,10 @@ std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext // These options should force fused MLA, but they don't. :/ // Adding unsafe math attribute to functions below do the trick. llvm::TargetOptions targetOptions; - targetOptions.UnsafeFPMath = true; + // targetOptions.UnsafeFPMath = true; targetOptions.AllowFPOpFusion = llvm::FPOpFusion::FPOpFusionMode::Fast; - targetMachine.reset(target->createTargetMachine(triple, + auto llvmTriple = llvm::Triple(triple); + targetMachine.reset(target->createTargetMachine(llvmTriple, cpuName, "+" + fpuName, targetOptions, diff --git a/src/plugins/intel_gpu/src/graph/generic_primitive.cpp b/src/plugins/intel_gpu/src/graph/generic_primitive.cpp index dbd1737f5ab060..883c0568aaf0e5 100644 --- a/src/plugins/intel_gpu/src/graph/generic_primitive.cpp +++ b/src/plugins/intel_gpu/src/graph/generic_primitive.cpp @@ -52,6 +52,6 @@ std::string generic_primitive_inst::to_string(generic_primitive_node const& node return primitive_description.str(); } -generic_primitive_inst::typed_primitive_inst(network& network, generic_primitive_node const& node) : parent(network, node) {} +generic_primitive_inst::typed_primitive_inst(network& network, generic_primitive_node const& node) : parent(network, node), node(&node) {} } // namespace cldnn diff --git a/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp b/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp index 035a8ee064d6f9..271b6548594b0b 100644 --- a/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp +++ b/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp @@ -3,7 +3,8 @@ // #include "generic_primitive_inst.h" -#include "implementation_map.hpp" +#include "generic_primitive.hpp" +#include "registry/implementation_map.hpp" #include "register.hpp" #include @@ -18,7 +19,7 @@ struct generic_primitive_impl : typed_primitive_impl { DECLARE_OBJECT_TYPE_SERIALIZATION(cldnn::common::generic_primitive_impl) std::unique_ptr clone() const override { - return make_unique(*this); + return std::make_unique(*this); } generic_primitive_impl() : parent() {} @@ -47,7 +48,7 @@ struct generic_primitive_impl : typed_primitive_impl { } static std::unique_ptr create(const generic_primitive_node& arg, const kernel_impl_params&) { - return make_unique(arg); + return std::make_unique(arg); } void init_kernels(const kernels_cache& , const kernel_impl_params&) override {} @@ -61,6 +62,11 @@ struct generic_primitive_impl : typed_primitive_impl { } }; +std::unique_ptr GenericPrimitiveImplementationManager::create_impl(const program_node& node, const kernel_impl_params& params) const { + assert(node.is_type()); + return generic_primitive_impl::create(static_cast(node), params); +} + namespace detail { attach_generic_primitive_common::attach_generic_primitive_common() { diff --git a/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.hpp b/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.hpp new file mode 100644 index 00000000000000..f51e7813f9cd3c --- /dev/null +++ b/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.hpp @@ -0,0 +1,36 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "registry/implementation_manager.hpp" +#include "program_node.h" + +#include + +namespace cldnn { + +namespace common { + +struct GenericPrimitiveImplementationManager : public ImplementationManager { + OV_GPU_PRIMITIVE_IMPL("common::generic_primitive") + GenericPrimitiveImplementationManager(shape_types shape_type, ValidateFunc vf = nullptr) : ImplementationManager(impl_types::common, shape_type, vf) {} + + std::unique_ptr create_impl(const program_node& node, const kernel_impl_params& params) const override; + + // in_out_fmts_t query_formats(const program_node& node) const override { + // std::vector in_fmts(node.get_dependencies().size(), format::any); + // std::vector out_fmts(node.get_outputs_count(), format::any); + + // for (size_t i = 0; i < node.get_dependencies().size(); i++) { + // size_t in_rank = node.get_input_layout(i).get_rank(); + // in_fmts[i] = format::get_default_format(in_rank); + // } + // size_t out_rank = node.get_output_layout().get_rank(); + // out_fmts[0] = format::get_default_format(out_rank); + + // return {in_fmts, out_fmts}; + // } +}; + +} // namespace common +} // namespace cldnn diff --git a/src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h b/src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h index 723e0b3d5abd1e..d9ea8e59e6c9ed 100644 --- a/src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h +++ b/src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h @@ -35,6 +35,8 @@ class typed_primitive_inst : public typed_primitive_inst_base static std::string to_string(generic_primitive_node const& node); typed_primitive_inst(network& network, generic_primitive_node const& node); + + const generic_primitive_node* node; }; using generic_primitive_inst = typed_primitive_inst; diff --git a/src/plugins/intel_gpu/src/graph/registry/generic_primitive_impls.cpp b/src/plugins/intel_gpu/src/graph/registry/generic_primitive_impls.cpp new file mode 100644 index 00000000000000..c4ebd305f9937e --- /dev/null +++ b/src/plugins/intel_gpu/src/graph/registry/generic_primitive_impls.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "registry.hpp" +#include "intel_gpu/primitives/generic_primitive.hpp" +#include "primitive_inst.h" + +#if OV_GPU_WITH_COMMON + #include "impls/common/generic_primitive.hpp" +#endif + + +namespace ov::intel_gpu { + +using namespace cldnn; + +const std::vector>& Registry::get_implementations() { + static const std::vector> impls = { + OV_GPU_CREATE_INSTANCE_COMMON(common::GenericPrimitiveImplementationManager, shape_types::static_shape) + OV_GPU_CREATE_INSTANCE_COMMON(common::GenericPrimitiveImplementationManager, shape_types::dynamic_shape) + }; + + return impls; +} + +} // namespace ov::intel_gpu diff --git a/src/plugins/intel_gpu/src/graph/registry/registry.hpp b/src/plugins/intel_gpu/src/graph/registry/registry.hpp index 0bdbca846c3a18..ce165235345ea8 100644 --- a/src/plugins/intel_gpu/src/graph/registry/registry.hpp +++ b/src/plugins/intel_gpu/src/graph/registry/registry.hpp @@ -141,6 +141,7 @@ REGISTER_IMPLS(fully_connected); REGISTER_IMPLS(gather); REGISTER_IMPLS(gather_nd); REGISTER_IMPLS(gemm); +REGISTER_IMPLS(generic_primitive); REGISTER_IMPLS(group_normalization); REGISTER_IMPLS(loop); REGISTER_IMPLS(lora); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp index 8646fa705309df..d53fbf401bf702 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp @@ -18,8 +18,9 @@ using testing::ElementsAreArray; -static std::string model_full_path(const char* path) { - return ov::util::make_path(TEST_MODELS_DIR, path); +static std::string model_full_path(const std::string& path) { + std::string base = TEST_MODELS_DIR; + return ov::util::make_path(base + "/" + path); } template @@ -172,8 +173,8 @@ static std::map allocate_input_tensors( } TEST(MLIRExecution, SimpleMatmulf32) { - if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") - GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + // if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") + // GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; ov::Core core; auto model = core.read_model( @@ -216,8 +217,8 @@ TEST(MLIRExecution, SimpleMatmulf32) { } TEST(MLIRExecution, SimpleMatmulf32CLBuffer) { - if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") - GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + // if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") + // GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; ov::Core core; auto model = core.read_model( @@ -260,8 +261,8 @@ TEST(MLIRExecution, SimpleMatmulf32CLBuffer) { } TEST(MLIRExecution, SimpleMatmulf16) { - if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") - GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + // if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") + // GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; ov::Core core; auto model = core.read_model( From 0f05a42cc4092cb5439f71a7e9198758bb7c2f79 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Wed, 4 Feb 2026 18:26:39 +0000 Subject: [PATCH 045/121] Fix GC build Signed-off-by: dchigarev --- CMakeLists.txt | 3 + cmake/graph-compiler.cmake | 11 +- .../src/transformations/mlir/convert.cpp | 8 +- .../src/transformations/mlir/mlir_op.cpp | 3 +- .../src/transformations/mlir/op/sdpa.cpp | 37 +++++ .../src/transformations/mlir/op/sdpa.hpp | 23 +++ .../functional/mlir_op/models/sdpa_test.xml | 134 ++++++++++++++++++ .../tests/functional/mlir_op/sanity_tests.cpp | 27 +++- 8 files changed, 235 insertions(+), 11 deletions(-) create mode 100644 src/common/transformations/src/transformations/mlir/op/sdpa.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/sdpa.hpp create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/models/sdpa_test.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a08435f387a3a..548d86334385a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,6 +154,9 @@ include(cmake/tpp-mlir.cmake) # Graph Compiler # if (ENABLE_GRAPH_COMPILER) + find_package(LLVM REQUIRED CONFIG) + find_package(MLIR REQUIRED CONFIG) + include(cmake/graph-compiler.cmake) add_definitions(-DGRAPH_COMPILER) endif() diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index 01dae4cb144aeb..622d47613a7d4c 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -56,8 +56,7 @@ if (NOT DEFINED GRAPH_COMPILER_LIBS) set(GRAPH_COMPILER_LIBS GcInterface - GcJitWrapper - GcCpuRuntime + # MLIRLinalgx ) if (ENABLE_INTEL_GPU) @@ -67,5 +66,13 @@ if (NOT DEFINED GRAPH_COMPILER_LIBS) set_property(GLOBAL PROPERTY GRAPH_COMPILER_LIBS ${GRAPH_COMPILER_LIBS}) endif () +if (NOT TARGET GcInterface) + if (DEFINED GraphCompiler_DIR AND EXISTS "${GraphCompiler_DIR}/GraphCompilerTargets.cmake") + include("${GraphCompiler_DIR}/GraphCompilerTargets.cmake") + elseif (DEFINED GraphCompiler_ROOT) + include("${GraphCompiler_ROOT}/lib/cmake/GraphCompiler/GraphCompilerTargets.cmake") + endif() +endif() + get_target_property(GRAPH_COMPILER_INCLUDES GcInterface INTERFACE_INCLUDE_DIRECTORIES) get_target_property(GRAPH_COMPILER_COMPILE_OPTIONS GcInterface INTERFACE_COMPILE_OPTIONS) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index 9011a3ffaaaeff..c6e7d3818466d2 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -62,7 +62,8 @@ #include "mlir/Target/LLVMIR/ModuleTranslation.h" #ifdef GRAPH_COMPILER -#include "gc/ExecutionEngine/Driver/Driver.h" +#include "gc/Transforms/Passes.h" +// #include "gc/Dialect/Linalgx/IR/LinalgxDialect.h" #endif #ifdef TPP_MLIR // If TPP is available @@ -83,6 +84,7 @@ #include "op/squeeze.hpp" #include "op/transpose.hpp" #include "op/unsqueeze.hpp" +#include "op/sdpa.hpp" #include "op/binary_eltwise.hpp" #include "openvino/core/dimension.hpp" #include "openvino/core/rt_info.hpp" @@ -320,6 +322,7 @@ void injectMLIR(std::shared_ptr model, using namespace ov::op; manager.set_per_pass_validation(false); manager.register_pass(); + manager.register_pass(); manager.register_pass>(); manager.register_pass>(); manager.register_pass>(); @@ -343,6 +346,7 @@ void loadDialects(MLIRContext* context) { context->loadDialect(); context->loadDialect(); context->loadDialect(); + // context->loadDialect(); context->loadDialect(); context->loadDialect(); } @@ -368,7 +372,7 @@ MLIRContext* get_shared_mlir_context(MlirMode mode) { #ifdef GRAPH_COMPILER if (mode == MLIR_MODE_GC || mode == MLIR_MODE_GC_GPU) { OPENVINO_MLIR_DEBUG_PRINT("GC\n"); - context = std::make_shared(gc::initCompilerAndGetDialects()); + context = std::make_shared(gc::getDialectRegistry()); } else { #endif // Initialize the LLVM machinery diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 3e5e409cb00f33..30ab75655d16e8 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -93,7 +93,8 @@ void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module, #endif #ifdef GRAPH_COMPILER case ov::mlir::MLIR_MODE_GC: { - gc::populateCPUPipeline(pm); + gc::GPUPipelineOptions opts; + gc::populateGPUPipeline(pm, opts); break; } #endif diff --git a/src/common/transformations/src/transformations/mlir/op/sdpa.cpp b/src/common/transformations/src/transformations/mlir/op/sdpa.cpp new file mode 100644 index 00000000000000..35b41bb4e94d75 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/sdpa.cpp @@ -0,0 +1,37 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/Dialect/Linalg/Passes.h" + +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" +#include + +#include "sdpa.hpp" +#include "../convert_common.hpp" + +namespace { + +using namespace ov::mlir; + +struct ConvertSDPA { + void operator()(ConversionContext& context, NodePtr node) { + std::cout << "Hello from convertSDPA!\n" << std::endl; + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +SDPAPattern::SDPAPattern() + : MarkPattern(wrap_type(), ConvertSDPA()) {} + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/sdpa.hpp b/src/common/transformations/src/transformations/mlir/op/sdpa.hpp new file mode 100644 index 00000000000000..1d38d1af3e99a7 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/sdpa.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +class SDPAPattern : public MarkPattern { +public: + OPENVINO_RTTI("SDPAPattern", "0"); + SDPAPattern(); +}; + +} // namespace mlir +} // namespace ov diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/sdpa_test.xml b/src/plugins/intel_gpu/tests/functional/mlir_op/models/sdpa_test.xml new file mode 100644 index 00000000000000..eaceb435bf3bd6 --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/models/sdpa_test.xml @@ -0,0 +1,134 @@ + + + + + + + + + + 1 + 64 + 80 + + + + + + + + + + 1 + 128 + 80 + + + + + + + + + + 1 + 128 + 80 + + + + + + + + + + + 1 + 1 + 128 + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 64 + 80 + + + 1 + 128 + 80 + + + 1 + 128 + 80 + + + 1 + 1 + 128 + + + + + + + + + 1 + 64 + 80 + + + + + + + + + 1 + 64 + 80 + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp index d53fbf401bf702..595fc2633a8677 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp @@ -172,9 +172,24 @@ static std::map allocate_input_tensors( return input_tensors; } +TEST(MLIRExecution, SimpleSDPA) { + if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") + GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + + ov::Core core; + auto model = core.read_model( + model_full_path("sdpa_test.xml")); + + ov::AnyMap device_config; + device_config[ov::hint::performance_mode.name()] = ov::hint::PerformanceMode::THROUGHPUT; + device_config[ov::enable_profiling.name()] = false; + + auto compiled_model = core.compile_model(model, "GPU", device_config); +} + TEST(MLIRExecution, SimpleMatmulf32) { - // if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") - // GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") + GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; ov::Core core; auto model = core.read_model( @@ -217,8 +232,8 @@ TEST(MLIRExecution, SimpleMatmulf32) { } TEST(MLIRExecution, SimpleMatmulf32CLBuffer) { - // if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") - // GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") + GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; ov::Core core; auto model = core.read_model( @@ -261,8 +276,8 @@ TEST(MLIRExecution, SimpleMatmulf32CLBuffer) { } TEST(MLIRExecution, SimpleMatmulf16) { - // if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") - // GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") + GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; ov::Core core; auto model = core.read_model( From 1f39cba413f1fdc5683c2c95beb3cfc18cd94516 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Wed, 4 Feb 2026 18:35:20 +0000 Subject: [PATCH 046/121] Add gc-build instructions Signed-off-by: dchigarev --- GC_BUILD.MD | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 GC_BUILD.MD diff --git a/GC_BUILD.MD b/GC_BUILD.MD new file mode 100644 index 00000000000000..afac3d9376b555 --- /dev/null +++ b/GC_BUILD.MD @@ -0,0 +1,72 @@ +### Tested on x1-spr 'Triton (GPU, Agama 2521.10, DLE 2025.1.1, Ubuntu 22.04)' profile + +#### step 1: build llvm (tested on 2634a2bda1db92ab5324a47459ee7f23e531ce53) +Important! RTTI has to be enabled! +``` +cmake -G Ninja ../llvm \ + -DLLVM_ENABLE_DUMP=1 \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_ENABLE_ASSERTIONS=true \ + -DLLVM_ENABLE_PROJECTS="mlir;lld" \ + -DLLVM_TARGETS_TO_BUILD="X86;SPIRV" \ + -DLLVM_INSTALL_UTILS=true \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DLLVM_ENABLE_RTTI=ON \ + -DLLVM_ENABLE_EH=ON \ + -DCMAKE_INSTALL_PREFIX=/home/jovyan/llvm/curr_install + +cmake --build . --target install +``` + +#### step 2: install opencl-headers & install llvm-env-vars +``` +sudo apt install -y intel-opencl-icd opencl-c-headers ocl-icd-opencl-dev +export LLVM_INST_PATH=/home/jovyan/llvm/build/ +``` + +#### step 3: build graph-compiler + +``` +# clone +git clone https://github.com/intel/graph-compiler.git +cd graph-compiler +git checkout AndreyPavlenko/ov +mkdir build && cd build + +# install nanobind +pip install nanobind + +# build +cmake ../ -G Ninja \ + -DLLVM_DIR=$LLVM_INST_PATH/lib/cmake/llvm \ + -DMLIR_DIR=$LLVM_INST_PATH/lib/cmake/mlir \ + -DCMAKE_INSTALL_PREFIX=/home/jovyan/graph-compiler/install + +cmake --build . --target install +``` + +#### step 4: build openvino + +``` +git clone https://github.com/intel-sandbox/openvino-gc +cd openvino +git checkout mlir-gc-integration + +mkdir build && cd build +cmake ../ -G Ninja -DLLVM_DIR=$LLVM_INST_PATH/lib/cmake/llvm \ + -DMLIR_DIR=$LLVM_INST_PATH/lib/cmake/mlir \ + -DENABLE_GRAPH_COMPILER=ON \ + -DENABLE_INTEL_GPU=ON \ + -DENABLE_TESTS=ON \ + -DENABLE_ONEDNN_FOR_GPU=OFF \ + -DENABLE_INTEL_CPU=OFF \ + -DCMAKE_CXX_FLAGS="-DOV_GPU_OPENCL_HPP_HAS_UUID -DOV_GPU_OPENCL_HPP_HAS_BUS_INFO" \ + -DGraphCompiler_DIR=/home/jovyan/graph-compiler/install/lib/cmake/GraphCompiler +cmake --build . -j16 +``` + +#### step 5: run sanity-mlir tests and dump mlir + +``` +OV_MLIR_DEBUG=1 OV_MLIR_MODE=GC ./bin/intel64/Release/ov_gpu_func_tests --gtest_filter=MLIRExecution.SimpleMatmulf32 +``` \ No newline at end of file From 01f7f22eedbcb6de3c84b1e341cfacf354e3ff7c Mon Sep 17 00:00:00 2001 From: dchigarev Date: Mon, 9 Feb 2026 12:57:55 +0000 Subject: [PATCH 047/121] Add basic ov::SDPA -> linalgx converter Signed-off-by: dchigarev --- cmake/graph-compiler.cmake | 2 +- .../src/transformations/mlir/convert.cpp | 8 +-- .../src/transformations/mlir/op/sdpa.cpp | 52 ++++++++++++++++++- .../tests/functional/mlir_op/sanity_tests.cpp | 28 +++++++--- 4 files changed, 75 insertions(+), 15 deletions(-) diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index 622d47613a7d4c..51c019131deec2 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -56,7 +56,7 @@ if (NOT DEFINED GRAPH_COMPILER_LIBS) set(GRAPH_COMPILER_LIBS GcInterface - # MLIRLinalgx + MLIRLinalgx ) if (ENABLE_INTEL_GPU) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index c6e7d3818466d2..8d315834f24830 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -63,7 +63,6 @@ #ifdef GRAPH_COMPILER #include "gc/Transforms/Passes.h" -// #include "gc/Dialect/Linalgx/IR/LinalgxDialect.h" #endif #ifdef TPP_MLIR // If TPP is available @@ -343,12 +342,7 @@ void injectMLIR(std::shared_ptr model, } void loadDialects(MLIRContext* context) { - context->loadDialect(); - context->loadDialect(); - context->loadDialect(); - // context->loadDialect(); - context->loadDialect(); - context->loadDialect(); + context->loadAllAvailableDialects(); } MLIRContext* get_shared_mlir_context(MlirMode mode) { diff --git a/src/common/transformations/src/transformations/mlir/op/sdpa.cpp b/src/common/transformations/src/transformations/mlir/op/sdpa.cpp index 35b41bb4e94d75..3fd83178e4f6b5 100644 --- a/src/common/transformations/src/transformations/mlir/op/sdpa.cpp +++ b/src/common/transformations/src/transformations/mlir/op/sdpa.cpp @@ -9,6 +9,12 @@ #include "openvino/pass/pattern/op/wrap_type.hpp" #include +#include "mlir/Dialect/Arith/IR/Arith.h" +#include "mlir/Dialect/Math/IR/Math.h" +#include "gc/Dialect/Linalgx/LinalgxDialect.h" +#include "gc/Dialect/Linalgx/LinalgxOps.h" +#include "mlir/IR/AffineExpr.h" + #include "sdpa.hpp" #include "../convert_common.hpp" @@ -17,8 +23,52 @@ namespace { using namespace ov::mlir; struct ConvertSDPA { + static SmallVector getStandardAttentionIndexingMaps(MLIRContext *ctx, + bool hasMask) { + AffineExpr m, n, k1, k2; + bindDims(ctx, m, n, k1, k2); + + auto qMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, {m, k1}, ctx); + auto kMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, {k2, k1}, ctx); + auto vMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, {k2, n}, ctx); + auto sMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, ctx); + auto rMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, {m, n}, ctx); + if (hasMask) { + // Add mask map only if it exists + auto mMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, {m, k2}, ctx); + return {qMap, kMap, vMap, sMap, mMap, rMap}; + } + return {qMap, kMap, vMap, sMap, rMap}; + } + void operator()(ConversionContext& context, NodePtr node) { - std::cout << "Hello from convertSDPA!\n" << std::endl; + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + // TODO: Support broadcasts + const auto inputs = context.getInputs(node); + const auto ov_output_element_type = node->get_output_element_type(0); + const auto ov_output_shape = node->get_output_partial_shape(0); + auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); + auto dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); + auto empty = builder.create(loc, outType, dynamic_dimensions); + auto zero = getConstant(builder, ov_output_element_type, 0); + auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); + + mlir::SmallVector ins{inputs[0], inputs[1], inputs[2]}; + mlir::SmallVector outs{fill.getResult(0)}; + + auto matmul_node = std::dynamic_pointer_cast(node); + assert(matmul_node); + + Operation* sdpa; + SmallVector indexingMaps = + getStandardAttentionIndexingMaps(context.context, false); + // FIXME: extract actual scale + Value scale = getConstant(builder, ov_output_element_type, 1.0f); + sdpa = builder.create( + loc, outs[0].getType(), inputs[0], inputs[1], inputs[2], scale, outs[0], + builder.getAffineMapArrayAttr(indexingMaps), /*mask=*/nullptr); + context.addOutputs(node, sdpa); } }; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp index 595fc2633a8677..6c2caf289622ef 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp @@ -15,6 +15,8 @@ #include #include "opencl_helper_instance.hpp" #include "openvino/core/preprocess/pre_post_process.hpp" +#include "openvino/core/partial_shape.hpp" +#include "openvino/op/scaled_dot_product_attention.hpp" using testing::ElementsAreArray; @@ -172,13 +174,27 @@ static std::map allocate_input_tensors( return input_tensors; } -TEST(MLIRExecution, SimpleSDPA) { - if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") - GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; - +TEST(MLIRExecution, CompileBasicSDPA) { + const ov::PartialShape query_shape{2, 2, 4096, 64}; + const ov::PartialShape key_shape{2, 2, 4096, 64}; + const ov::PartialShape value_shape{2, 2, 4096, 64}; + + const auto query = std::make_shared(ov::element::f16, query_shape); + const auto key = std::make_shared(ov::element::f16, key_shape); + const auto value = std::make_shared(ov::element::f16, value_shape); + const auto sdpa_mask_const = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{0.0f}); + const auto sdpa_scale_const = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{2.0f}); + const auto scale_const = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{8.0f}); + const auto casual = false; + const auto sdpa = std::make_shared(query, + key, + value, + sdpa_mask_const, + sdpa_scale_const, + casual); + + auto model = std::make_shared(ov::OutputVector{sdpa}, ov::ParameterVector{query, key, value}); ov::Core core; - auto model = core.read_model( - model_full_path("sdpa_test.xml")); ov::AnyMap device_config; device_config[ov::hint::performance_mode.name()] = ov::hint::PerformanceMode::THROUGHPUT; From cafa32d3f47f288fe0c1c9b0f109a1dd3666a4a1 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Thu, 26 Feb 2026 11:45:02 +0000 Subject: [PATCH 048/121] Fix sanity tests Signed-off-by: dchigarev --- .../mlir_op/models/matmul_64_128_f16.xml | 2 +- .../mlir_op/models/matmul_64_128_f32.xml | 2 +- .../tests/functional/mlir_op/sanity_tests.cpp | 22 ++++++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml index ede68f3452a1c2..7164dcf7a2e695 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml @@ -10,7 +10,7 @@ - + diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.xml b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.xml index 202d057ad4047f..0d877ac9098671 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.xml +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.xml @@ -10,7 +10,7 @@ - + diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp index 6c2caf289622ef..0937e25901af6a 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp @@ -175,6 +175,10 @@ static std::map allocate_input_tensors( } TEST(MLIRExecution, CompileBasicSDPA) { + auto mode = ov::util::getenv_string("OV_MLIR_MODE"); + if (mode != "GC_GPU" && mode != "GC") + GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " + << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; const ov::PartialShape query_shape{2, 2, 4096, 64}; const ov::PartialShape key_shape{2, 2, 4096, 64}; const ov::PartialShape value_shape{2, 2, 4096, 64}; @@ -204,8 +208,10 @@ TEST(MLIRExecution, CompileBasicSDPA) { } TEST(MLIRExecution, SimpleMatmulf32) { - if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") - GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + auto mode = ov::util::getenv_string("OV_MLIR_MODE"); + if (mode != "GC_GPU" && mode != "GC") + GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " + << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; ov::Core core; auto model = core.read_model( @@ -248,8 +254,10 @@ TEST(MLIRExecution, SimpleMatmulf32) { } TEST(MLIRExecution, SimpleMatmulf32CLBuffer) { - if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") - GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + auto mode = ov::util::getenv_string("OV_MLIR_MODE"); + if (mode != "GC_GPU" && mode != "GC") + GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " + << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; ov::Core core; auto model = core.read_model( @@ -292,8 +300,10 @@ TEST(MLIRExecution, SimpleMatmulf32CLBuffer) { } TEST(MLIRExecution, SimpleMatmulf16) { - if (ov::util::getenv_string("OV_MLIR_MODE") != "GC_GPU") - GTEST_SKIP() << "This test is only for GC_GPU MLIR mode. Set 'OV_MLIR_MODE' env variable to 'GC_GPU'"; + auto mode = ov::util::getenv_string("OV_MLIR_MODE"); + if (mode != "GC_GPU" && mode != "GC") + GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " + << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; ov::Core core; auto model = core.read_model( From 8d3267ddc7d197c8e29f4e79236e95fc6e4b2f83 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Thu, 26 Feb 2026 12:47:05 +0000 Subject: [PATCH 049/121] Remove add Signed-off-by: dchigarev --- .../mlir_op/models/matmul_64_128_f16.xml | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml index 7164dcf7a2e695..c2ca6227b5f811 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml @@ -38,25 +38,6 @@ - - - - - 64 - 128 - - - 64 - 128 - - - - - 64 - 128 - - - @@ -69,9 +50,7 @@ - - - + From 5b3b9b1869e03320134cd6300f10bbc91076d84e Mon Sep 17 00:00:00 2001 From: dchigarev Date: Thu, 26 Feb 2026 14:00:33 +0000 Subject: [PATCH 050/121] Enable GPU executor Signed-off-by: dchigarev --- CMakeLists.txt | 1 + .../src/transformations/mlir/convert.cpp | 2 +- .../src/transformations/mlir/mlir_op.cpp | 10 +++++----- .../src/transformations/mlir/mlir_op.hpp | 6 +++--- .../functional/mlir_op/models/matmul_64_128_f16.xml | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 548d86334385a5..2e07281f641b9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,7 @@ if (ENABLE_GRAPH_COMPILER) include(cmake/graph-compiler.cmake) add_definitions(-DGRAPH_COMPILER) + add_definitions(-DGC_USE_GPU) endif() # diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index 8d315834f24830..cd2aef5f0fce7e 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -457,7 +457,7 @@ void ov::pass::transformMLIR(std::shared_ptr model, "[ ERROR ] OpenVINO wasn't compiled with GRAPH_COMPILER support, " "but OV_MLIR_MODE environment variable is set to GC_GPU."); #endif -#ifndef GC_USE_IMEX // GC_GPU requires IMEX support +#ifndef GC_USE_GPU // GC_GPU requires IMEX support OPENVINO_THROW( "[ ERROR ] GraphCompiler wasn't compiled with IMEX support (-DGC_ENABLE_IMEX), " "but OV_MLIR_MODE environment variable is set to GC_GPU."); diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 30ab75655d16e8..f719ba30091401 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -65,7 +65,7 @@ #ifdef GRAPH_COMPILER #include "gc/Transforms/Passes.h" -#ifdef GC_USE_IMEX // GC_GPU requires IMEX support +#ifdef GC_USE_GPU // GC_GPU requires IMEX support #include "gc/Utils/Error.h" #include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" #include "openvino/runtime/intel_gpu/remote_properties.hpp" @@ -303,7 +303,7 @@ std::shared_ptr MLIREvaluateBase::create(OwningOpRef MlirMode mode, std::shared_ptr loweringContext) { switch (mode) { - #ifdef GC_USE_IMEX // GC_GPU requires IMEX support + #ifdef GC_USE_GPU // GC_GPU requires IMEX support case MLIR_MODE_GC_GPU: return std::make_shared(std::move(module), loweringContext); #endif @@ -316,7 +316,7 @@ std::shared_ptr MLIREvaluateBase::create(OwningOpRef } } -#ifdef GC_USE_IMEX // GC_GPU requires IMEX support +#ifdef GC_USE_GPU // GC_GPU requires IMEX support cl_device_id extract_device_from_context(cl_context context) { size_t devices_size; @@ -346,7 +346,7 @@ MLIREvaluateGcGPU::MLIREvaluateGcGPU(OwningOpRef _module, std::s "-----------------------------------------\n"); gc::gpu::OclModuleBuilderOpts opts; - OPENVINO_MLIR_DEBUG(opts.printIr = true); + OPENVINO_MLIR_DEBUG(opts.dumpIr = true); gc::gpu::OclModuleBuilder builder(std::move(_module), opts); auto it = loweringContext->find(ov::intel_gpu::ocl_context.name()); @@ -447,7 +447,7 @@ gc::gpu::OclContext MLIREvaluateGcGPU::build_ocl_context(const ov::EvaluationCon waitListLen, reinterpret_cast(waitList.data())); } -#endif // GC_USE_IMEX +#endif // GC_USE_GPU MLIREvaluate::MLIREvaluate(OwningOpRef _module, MlirMode mode) : module(std::move(_module)) { diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.hpp b/src/common/transformations/src/transformations/mlir/mlir_op.hpp index 1429f8659bf2b8..918ab4a7270024 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.hpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.hpp @@ -15,7 +15,7 @@ #include "convert_common.hpp" -#ifdef GC_USE_IMEX // GC_GPU requires IMEX support +#ifdef GC_USE_GPU // GC_GPU requires IMEX support #include "gc/Utils/Error.h" #include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" #endif @@ -50,7 +50,7 @@ class MLIREvaluateBase { virtual ~MLIREvaluateBase() = default; }; -#ifdef GC_USE_IMEX // GC_GPU requires IMEX support +#ifdef GC_USE_GPU // GC_GPU requires IMEX support class MLIREvaluateGcGPU : public MLIREvaluateBase { std::shared_ptr module; @@ -67,7 +67,7 @@ class MLIREvaluateGcGPU : public MLIREvaluateBase { static void maybe_set_result_event(const ov::EvaluationContext& evaluationContext, gc::gpu::OclContext& ctx); }; -#endif // GC_USE_IMEX +#endif // GC_USE_GPU class MLIREvaluate : public MLIREvaluateBase { OwningOpRef module; // FIXME: needs to be kept? diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml index c2ca6227b5f811..94f6c9b21b14f4 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml @@ -11,7 +11,7 @@ - + 128 From 38e0a79e73274a91e38de3a70c1ddac526f2506f Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Fri, 27 Feb 2026 00:18:16 +0000 Subject: [PATCH 051/121] Fixes --- .../src/transformations/mlir/convert.cpp | 4 +- .../src/transformations/mlir/mlir_op.cpp | 7 ++-- .../intel_gpu/src/plugin/ops/mlir_op.cpp | 9 +++-- .../tests/functional/mlir_op/sanity_tests.cpp | 40 ++++++++++++++----- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index cd2aef5f0fce7e..52a18d8407b590 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -457,9 +457,9 @@ void ov::pass::transformMLIR(std::shared_ptr model, "[ ERROR ] OpenVINO wasn't compiled with GRAPH_COMPILER support, " "but OV_MLIR_MODE environment variable is set to GC_GPU."); #endif -#ifndef GC_USE_GPU // GC_GPU requires IMEX support +#ifndef GC_USE_GPU OPENVINO_THROW( - "[ ERROR ] GraphCompiler wasn't compiled with IMEX support (-DGC_ENABLE_IMEX), " + "[ ERROR ] GraphCompiler wasn't compiled with Graph Compiler support (-DENABLE_GRAPH_COMPILER), " "but OV_MLIR_MODE environment variable is set to GC_GPU."); #endif mode = MLIR_MODE_GC_GPU; diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index f719ba30091401..a22f04b2cc2d85 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -65,7 +65,7 @@ #ifdef GRAPH_COMPILER #include "gc/Transforms/Passes.h" -#ifdef GC_USE_GPU // GC_GPU requires IMEX support +#ifdef GC_USE_GPU #include "gc/Utils/Error.h" #include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" #include "openvino/runtime/intel_gpu/remote_properties.hpp" @@ -303,7 +303,7 @@ std::shared_ptr MLIREvaluateBase::create(OwningOpRef MlirMode mode, std::shared_ptr loweringContext) { switch (mode) { - #ifdef GC_USE_GPU // GC_GPU requires IMEX support + #ifdef GC_USE_GPU case MLIR_MODE_GC_GPU: return std::make_shared(std::move(module), loweringContext); #endif @@ -316,7 +316,7 @@ std::shared_ptr MLIREvaluateBase::create(OwningOpRef } } -#ifdef GC_USE_GPU // GC_GPU requires IMEX support +#ifdef GC_USE_GPU cl_device_id extract_device_from_context(cl_context context) { size_t devices_size; @@ -385,6 +385,7 @@ bool MLIREvaluateGcGPU::invoke(const ov::TensorVector& inputs, ov::TensorVector& for (size_t i = 0, j = inputs.size(); i < outputs.size(); ++i, ++j) { exec.arg(outputs[i].data(), arg_types[j]); } + exec(ctx); maybe_set_result_event(evaluationContext, ctx); return true; diff --git a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp index 248034565a07ed..c9963cfb92c017 100644 --- a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp +++ b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp @@ -115,10 +115,13 @@ void CreateMLIRSubgraphOp(ProgramBuilder& p, const std::shared_ptr& input_shapes) -> std::vector { - // Dummy shape infer - return {input_shapes[0]}; + std::vector output_shapes; + for (size_t i = 0, n = op->get_output_size(); i < n; ++i) { + output_shapes.push_back(op->get_output_partial_shape(i)); + } + return output_shapes; }; auto inputs = p.GetInputInfo(op); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp index 0937e25901af6a..d6ace277be8060 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp @@ -25,6 +25,26 @@ static std::string model_full_path(const std::string& path) { return ov::util::make_path(base + "/" + path); } +template +static void multiply_matrices(const std::vector& matrix_a, const std::vector& matrix_b, + std::vector& result, size_t rows_a, size_t cols_a, size_t cols_b) { + // Initialize the result matrix with zero values (f32 accumulator) + std::vector tmp(result.size(), 0.0f); + + // Matrix multiplication logic using linear indexing + for (size_t i = 0; i < rows_a; ++i) { + for (size_t j = 0; j < cols_b; ++j) { + for (size_t k = 0; k < cols_a; ++k) { + tmp[i * cols_b + j] += matrix_a[i * cols_a + k] * matrix_b[k * cols_b + j]; + } + } + } + + for (size_t i = 0; i < result.size(); i++) { + result[i] = tmp[i]; // cast back to T(possibly f16) + } +} + template static void multiply_matrices_and_add_a(const std::vector& matrix_a, const std::vector& matrix_b, std::vector& result, size_t rows_a, size_t cols_a, size_t cols_b) { @@ -159,17 +179,18 @@ static std::map allocate_input_tensors( auto oclInstance = std::make_shared(oclContext.get()); std::map input_tensors; + int idx = 0; for (const auto& input : compiledModel.inputs()) { auto shape = input.get_shape(); auto size = ov::shape_size(shape); - std::vector input_values = broadcast_vector(inputValues[input.get_index()], size); + std::vector input_values = broadcast_vector(inputValues[idx], size); ov::Tensor tensor; if (use_usm) { tensor = allocate_usm_tensor(oclContext, oclInstance.get(), shape, input.get_element_type(), input_values); } else { tensor = allocate_cl_tensor(oclContext, oclInstance.get(), shape, input.get_element_type(), input_values, keep_alive); } - input_tensors.emplace(input.get_index(), tensor); + input_tensors.emplace(idx++, tensor); } return input_tensors; } @@ -316,11 +337,14 @@ TEST(MLIRExecution, SimpleMatmulf16) { auto compiled_model = core.compile_model(model, "GPU", device_config); - std::map> input_values_map; - input_values_map.emplace(0, std::vector(1, 0.5f)); std::vector keep_alive; - + std::vector matrix_a = broadcast_vector(std::vector(1, 3.5f), 64 * 128); + std::vector matrix_b = broadcast_vector(std::vector(1, 1.5f), 128 * 128); + // std::vector matrix_b = read_float_array_from_binary_file(model_full_path("matmul_64_128_f16.bin"), 2); + std::map> input_values_map; + input_values_map.emplace(0, matrix_b); + input_values_map.emplace(1, matrix_a); auto input_tensors = allocate_input_tensors(compiled_model, input_values_map, true, keep_alive); auto infer_req = compiled_model.create_infer_request(); @@ -333,11 +357,9 @@ TEST(MLIRExecution, SimpleMatmulf16) { ov::float16* result = reinterpret_cast(computed.data()); // compute reference result - std::vector matrix_a = broadcast_vector(input_values_map.at(0), 64 * 128); - std::vector matrix_b = read_float_array_from_binary_file(model_full_path("matmul_64_128_f16.bin"), 2); - ASSERT_EQ(matrix_b.size(), 128 * 128); + // ASSERT_EQ(matrix_b.size(), 128 * 128); std::vector reference_result(64 * 128); - multiply_matrices_and_add_a(matrix_a, matrix_b, reference_result, 64, 128, 128); + multiply_matrices(matrix_a, matrix_b, reference_result, 64, 128, 128); // compare result with the reference for (size_t i = 0; i < reference_result.size(); ++i) { From d983076a69c1545a9d85ecc22076959aa7d555ca Mon Sep 17 00:00:00 2001 From: dchigarev Date: Tue, 10 Mar 2026 15:26:26 +0000 Subject: [PATCH 052/121] Support scale & mask & 4d cases in SDPA conversion Signed-off-by: dchigarev --- .../src/transformations/mlir/op/sdpa.cpp | 117 +++++++++++++----- ...ed_dot_product_attention_decomposition.cpp | 5 + .../tests/functional/mlir_op/sanity_tests.cpp | 86 +++++++++++-- 3 files changed, 169 insertions(+), 39 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/op/sdpa.cpp b/src/common/transformations/src/transformations/mlir/op/sdpa.cpp index 3fd83178e4f6b5..0ed72604afb123 100644 --- a/src/common/transformations/src/transformations/mlir/op/sdpa.cpp +++ b/src/common/transformations/src/transformations/mlir/op/sdpa.cpp @@ -24,29 +24,98 @@ using namespace ov::mlir; struct ConvertSDPA { static SmallVector getStandardAttentionIndexingMaps(MLIRContext *ctx, - bool hasMask) { - AffineExpr m, n, k1, k2; - bindDims(ctx, m, n, k1, k2); - - auto qMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, {m, k1}, ctx); - auto kMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, {k2, k1}, ctx); - auto vMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, {k2, n}, ctx); - auto sMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, ctx); - auto rMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, {m, n}, ctx); - if (hasMask) { - // Add mask map only if it exists - auto mMap = AffineMap::get(/*dimCount=*/4, /*symbolCount=*/0, {m, k2}, ctx); - return {qMap, kMap, vMap, sMap, mMap, rMap}; + bool hasMask, + int rank) { + if (rank == 3) { + // 3D: (batch, seq, hidden) + AffineExpr batch, m, k1, k2, n; + bindDims(ctx, batch, m, k1, k2, n); + + auto qMap = AffineMap::get(/*dimCount=*/5, /*symbolCount=*/0, {batch, m, k1}, ctx); + auto kMap = AffineMap::get(/*dimCount=*/5, /*symbolCount=*/0, {batch, k2, k1}, ctx); + auto vMap = AffineMap::get(/*dimCount=*/5, /*symbolCount=*/0, {batch, k2, n}, ctx); + auto sMap = AffineMap::get(/*dimCount=*/5, /*symbolCount=*/0, ctx); + auto rMap = AffineMap::get(/*dimCount=*/5, /*symbolCount=*/0, {batch, m, n}, ctx); + if (hasMask) { + auto mMap = AffineMap::get(/*dimCount=*/5, /*symbolCount=*/0, {batch, m, k2}, ctx); + return {qMap, kMap, vMap, sMap, mMap, rMap}; + } + return {qMap, kMap, vMap, sMap, rMap}; + } else if (rank == 4) { + // 4D: (batch, head, seq, hidden) + AffineExpr batch, head, m, k1, k2, n; + bindDims(ctx, batch, head, m, k1, k2, n); + + auto qMap = AffineMap::get(/*dimCount=*/6, /*symbolCount=*/0, {batch, head, m, k1}, ctx); + auto kMap = AffineMap::get(/*dimCount=*/6, /*symbolCount=*/0, {batch, head, k2, k1}, ctx); + auto vMap = AffineMap::get(/*dimCount=*/6, /*symbolCount=*/0, {batch, head, k2, n}, ctx); + auto sMap = AffineMap::get(/*dimCount=*/6, /*symbolCount=*/0, ctx); + auto rMap = AffineMap::get(/*dimCount=*/6, /*symbolCount=*/0, {batch, head, m, n}, ctx); + if (hasMask) { + auto mMap = AffineMap::get(/*dimCount=*/6, /*symbolCount=*/0, {batch, head, m, k2}, ctx); + return {qMap, kMap, vMap, sMap, mMap, rMap}; + } + return {qMap, kMap, vMap, sMap, rMap}; } - return {qMap, kMap, vMap, sMap, rMap}; + // Should never reach here due to validation + return {}; } void operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); - // TODO: Support broadcasts const auto inputs = context.getInputs(node); + + // Validate input ranks + auto qShape = node->get_input_partial_shape(0); + auto kShape = node->get_input_partial_shape(1); + auto vShape = node->get_input_partial_shape(2); + + auto qRank = qShape.rank().get_length(); + auto kRank = kShape.rank().get_length(); + auto vRank = vShape.rank().get_length(); + + OPENVINO_ASSERT(qRank == kRank && qRank == vRank, + "SDPA: Query, Key, and Value must have equal ranks, but got Q rank=", + qRank, + ", K rank=", + kRank, + ", V rank=", + vRank); + + OPENVINO_ASSERT(qRank == 3 || qRank == 4, + "SDPA: Only 3D and 4D inputs are supported, but got rank=", + qRank); + + auto sdpa_node = std::dynamic_pointer_cast(node); + OPENVINO_ASSERT(sdpa_node, "Failed to cast to ScaledDotProductAttention"); + + const auto input_size = node->get_input_size(); + const bool causal = sdpa_node->get_causal(); + OPENVINO_ASSERT(!causal, "SDPA: Causal attention is not supported in this version"); + OPENVINO_ASSERT(input_size < 6, "SDPA: sink parameter is not supported"); + const auto ov_output_element_type = node->get_output_element_type(0); + + // Extract mask (input 3) if present and not causal + Value mask = nullptr; + bool hasMask = false; + if (input_size > 3 && !causal && node->get_input_partial_shape(3).rank().get_length() > 0) { + mask = inputs[3]; + hasMask = true; + } + + // Extract or compute scale (input 4) + Value scale; + if (input_size > 4) { + // Scale is provided as input tensor - extract scalar from 0-dimensional tensor + // For a scalar tensor (shape {}), tensor.extract with no indices extracts the value + scale = builder.create(loc, inputs[4], mlir::ValueRange{}); + } else { + // Default scale: 1.0 + scale = getConstant(builder, ov_output_element_type, 1.0); + } + const auto ov_output_shape = node->get_output_partial_shape(0); auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); auto dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); @@ -54,20 +123,12 @@ struct ConvertSDPA { auto zero = getConstant(builder, ov_output_element_type, 0); auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); - mlir::SmallVector ins{inputs[0], inputs[1], inputs[2]}; - mlir::SmallVector outs{fill.getResult(0)}; - - auto matmul_node = std::dynamic_pointer_cast(node); - assert(matmul_node); - - Operation* sdpa; SmallVector indexingMaps = - getStandardAttentionIndexingMaps(context.context, false); - // FIXME: extract actual scale - Value scale = getConstant(builder, ov_output_element_type, 1.0f); - sdpa = builder.create( - loc, outs[0].getType(), inputs[0], inputs[1], inputs[2], scale, outs[0], - builder.getAffineMapArrayAttr(indexingMaps), /*mask=*/nullptr); + getStandardAttentionIndexingMaps(context.context, hasMask, qRank); + + Operation* sdpa = builder.create( + loc, fill.getResult(0).getType(), inputs[0], inputs[1], inputs[2], scale, fill.getResult(0), + builder.getAffineMapArrayAttr(indexingMaps), mask); context.addOutputs(node, sdpa); } }; diff --git a/src/common/transformations/src/transformations/op_conversions/scaled_dot_product_attention_decomposition.cpp b/src/common/transformations/src/transformations/op_conversions/scaled_dot_product_attention_decomposition.cpp index 1cce88954de483..e80632677250e0 100644 --- a/src/common/transformations/src/transformations/op_conversions/scaled_dot_product_attention_decomposition.cpp +++ b/src/common/transformations/src/transformations/op_conversions/scaled_dot_product_attention_decomposition.cpp @@ -78,6 +78,11 @@ ov::pass::ScaledDotProductAttentionDecomposition::ScaledDotProductAttentionDecom auto pattern_node = ov::pass::pattern::wrap_type(); matcher_pass_callback callback = [OV_CAPTURE_CPY_AND_THIS](Matcher& m) { + // FIXME: unconditionally disabling the decomposition for now + // so we can always lower SDPA to linalgx.attention. We have to disable + // it harsh since the 'enable_sdpa_optimization=false' parameter that should + // disable this transformation doesn't work for in 'common_optimizations' pass. + return false; auto& pattern_to_output = m.get_pattern_value_map(); auto node = ov::as_type_ptr(pattern_to_output.at(pattern_node).get_node_shared_ptr()); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp index d6ace277be8060..01a31032bbd801 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp @@ -195,35 +195,99 @@ static std::map allocate_input_tensors( return input_tensors; } -TEST(MLIRExecution, CompileBasicSDPA) { +TEST(MLIRExecution, CompileSDPABasic) { auto mode = ov::util::getenv_string("OV_MLIR_MODE"); if (mode != "GC_GPU" && mode != "GC") GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; - const ov::PartialShape query_shape{2, 2, 4096, 64}; - const ov::PartialShape key_shape{2, 2, 4096, 64}; - const ov::PartialShape value_shape{2, 2, 4096, 64}; + const ov::PartialShape query_shape{4, 4096, 64}; + const ov::PartialShape key_shape{4, 4096, 64}; + const ov::PartialShape value_shape{4, 4096, 64}; const auto query = std::make_shared(ov::element::f16, query_shape); const auto key = std::make_shared(ov::element::f16, key_shape); const auto value = std::make_shared(ov::element::f16, value_shape); - const auto sdpa_mask_const = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{0.0f}); - const auto sdpa_scale_const = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{2.0f}); - const auto scale_const = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{8.0f}); + const auto casual = false; const auto sdpa = std::make_shared(query, key, value, - sdpa_mask_const, - sdpa_scale_const, casual); auto model = std::make_shared(ov::OutputVector{sdpa}, ov::ParameterVector{query, key, value}); ov::Core core; ov::AnyMap device_config; - device_config[ov::hint::performance_mode.name()] = ov::hint::PerformanceMode::THROUGHPUT; - device_config[ov::enable_profiling.name()] = false; + // disable sdpa-decomposition + device_config[ov::intel_gpu::hint::enable_sdpa_optimization.name()] = false; + + auto compiled_model = core.compile_model(model, "GPU", device_config); +} + +TEST(MLIRExecution, CompileSDPA4DWithMaskAndScale) { + auto mode = ov::util::getenv_string("OV_MLIR_MODE"); + if (mode != "GC_GPU" && mode != "GC") + GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " + << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; + const ov::PartialShape query_shape{2, 8, 4096, 64}; + const ov::PartialShape key_shape{2, 8, 4096, 64}; + const ov::PartialShape value_shape{2, 8, 4096, 64}; + const ov::PartialShape mask_shape{2, 8, 4096, 4096}; + + const auto query = std::make_shared(ov::element::f16, query_shape); + const auto key = std::make_shared(ov::element::f16, key_shape); + const auto value = std::make_shared(ov::element::f16, value_shape); + const auto mask = std::make_shared(ov::element::f16, mask_shape); + const auto scale_const = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{0.125f}); + + const auto casual = false; + const auto sdpa = std::make_shared(query, + key, + value, + mask, + scale_const, + casual); + + auto model = std::make_shared(ov::OutputVector{sdpa}, ov::ParameterVector{query, key, value, mask}); + ov::Core core; + + ov::AnyMap device_config; + // disable sdpa-decomposition + device_config[ov::intel_gpu::hint::enable_sdpa_optimization.name()] = false; + + auto compiled_model = core.compile_model(model, "GPU", device_config); +} + +TEST(MLIRExecution, CompileSDPAWithScaleNoMask) { + auto mode = ov::util::getenv_string("OV_MLIR_MODE"); + if (mode != "GC_GPU" && mode != "GC") + GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " + << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; + const ov::PartialShape query_shape{4, 4096, 64}; + const ov::PartialShape key_shape{4, 4096, 64}; + const ov::PartialShape value_shape{4, 4096, 64}; + + const auto query = std::make_shared(ov::element::f16, query_shape); + const auto key = std::make_shared(ov::element::f16, key_shape); + const auto value = std::make_shared(ov::element::f16, value_shape); + // Empty mask constant (scalar placeholder) + const auto empty_mask = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{0.0f}); + const auto scale_const = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{0.125f}); + + const auto casual = false; + const auto sdpa = std::make_shared(query, + key, + value, + empty_mask, + scale_const, + casual); + + auto model = std::make_shared(ov::OutputVector{sdpa}, ov::ParameterVector{query, key, value}); + ov::Core core; + + ov::AnyMap device_config; + // disable sdpa-decomposition + device_config[ov::intel_gpu::hint::enable_sdpa_optimization.name()] = false; auto compiled_model = core.compile_model(model, "GPU", device_config); } From 6b8e1155f3d068dc7c9610745bb014deb80ad2d3 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Thu, 12 Mar 2026 16:06:51 +0000 Subject: [PATCH 053/121] Matmul + add --- .../mlir_op/models/matmul_64_128_f16.xml | 25 +++++++++++++++++-- .../tests/functional/mlir_op/sanity_tests.cpp | 10 ++++---- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml index 94f6c9b21b14f4..7164dcf7a2e695 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml @@ -11,7 +11,7 @@ - + 128 @@ -38,6 +38,25 @@ + + + + + 64 + 128 + + + 64 + 128 + + + + + 64 + 128 + + + @@ -50,7 +69,9 @@ - + + + diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp index 01a31032bbd801..6b26a1b2e4464f 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp @@ -403,12 +403,12 @@ TEST(MLIRExecution, SimpleMatmulf16) { std::vector keep_alive; - std::vector matrix_a = broadcast_vector(std::vector(1, 3.5f), 64 * 128); - std::vector matrix_b = broadcast_vector(std::vector(1, 1.5f), 128 * 128); + std::vector matrix_a = broadcast_vector(std::vector(1, 1.5), 64 * 128); + std::vector matrix_b = broadcast_vector(std::vector(1, 3.5), 128 * 128); // std::vector matrix_b = read_float_array_from_binary_file(model_full_path("matmul_64_128_f16.bin"), 2); std::map> input_values_map; - input_values_map.emplace(0, matrix_b); - input_values_map.emplace(1, matrix_a); + input_values_map.emplace(0, matrix_a); + input_values_map.emplace(1, matrix_b); auto input_tensors = allocate_input_tensors(compiled_model, input_values_map, true, keep_alive); auto infer_req = compiled_model.create_infer_request(); @@ -423,7 +423,7 @@ TEST(MLIRExecution, SimpleMatmulf16) { // compute reference result // ASSERT_EQ(matrix_b.size(), 128 * 128); std::vector reference_result(64 * 128); - multiply_matrices(matrix_a, matrix_b, reference_result, 64, 128, 128); + multiply_matrices_and_add_a(matrix_a, matrix_b, reference_result, 64, 128, 128); // compare result with the reference for (size_t i = 0; i < reference_result.size(); ++i) { From 89f6242d6a931b6229590bd2413f2168adf91053 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Fri, 13 Mar 2026 01:15:49 +0000 Subject: [PATCH 054/121] Added option for dynamic linking with LLVM and GC --- cmake/graph-compiler.cmake | 80 ++++++-------------------------------- src/cmake/openvino.cmake | 6 ++- 2 files changed, 16 insertions(+), 70 deletions(-) diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index 51c019131deec2..a99a847ce3daf5 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -1,78 +1,20 @@ get_property(GRAPH_COMPILER_LIBS GLOBAL PROPERTY GRAPH_COMPILER_LIBS) if (NOT DEFINED GRAPH_COMPILER_LIBS) - # The FetchContent_Declare(FIND_PACKAGE_ARGS) is supported since CMake 3.24. For the prior - # versions, using find_package() first. If the package is not found, then using FetchContent. - if (CMAKE_VERSION VERSION_LESS "3.24") - find_package(GraphCompiler QUIET) - else () - set(GC_FETCH_CONTENT_ARGS FIND_PACKAGE_ARGS NAMES GraphCompiler) - endif () - - if (NOT GraphCompiler_FOUND) - include(FetchContent) - - FetchContent_Declare( - GC - GIT_REPOSITORY https://github.com/intel/graph-compiler.git - GIT_TAG main - ${GC_FETCH_CONTENT_ARGS} - ) - - set(GC_ENABLE_IMEX ${ENABLE_INTEL_GPU}) - set(GC_ENABLE_TOOLS OFF) - set(GC_ENABLE_TEST OFF) - set(GC_ENABLE_DNNL_API OFF) - set(GC_ENABLE_LEGACY OFF) - set(GC_ENABLE_BINDINGS_PYTHON OFF) - set(OV_BUILD_SHARED_LIBS_TMP ${BUILD_SHARED_LIBS}) - set(BUILD_SHARED_LIBS OFF) - - FetchContent_GetProperties(GC) - if(NOT GC_POPULATED) - FetchContent_Populate(GC) - endif() - if(NOT DEFINED LLVM_DIR OR NOT DEFINED MLIR_DIR) - execute_process( - COMMAND /bin/bash ./scripts/compile.sh --dev --llvm --imex - WORKING_DIRECTORY ${gc_SOURCE_DIR} - ) - - SET(LLVM_INST_PATH "${gc_SOURCE_DIR}/externals/llvm-project/build") - SET(LLVM_DIR "${LLVM_INST_PATH}/lib/cmake/llvm") - SET(MLIR_DIR "${LLVM_INST_PATH}/lib/cmake/mlir") - add_definitions(-DMLIR_RUNNER_UTILS_PATH=${LLVM_INST_PATH}/lib) - add_subdirectory(${gc_SOURCE_DIR} ${gc_BINARY_DIR} EXCLUDE_FROM_ALL) - endif() - - FetchContent_MakeAvailable(GC) - set(BUILD_SHARED_LIBS ${OV_BUILD_SHARED_LIBS_TMP}) - FetchContent_GetProperties(GC BINARY_DIR gc_BINARY_DIR) - ov_cpack_add_component(GcCpuRuntime HIDDEN) - set(GC_CPU_RUNTIME_PATH ${gc_BINARY_DIR}/lib/libGcCpuRuntime.so) - install(FILES ${GC_CPU_RUNTIME_PATH} DESTINATION ${OV_CPACK_RUNTIMEDIR} COMPONENT GcCpuRuntime) - # a hack to not bother with actual file extension - install(FILES ${GC_CPU_RUNTIME_PATH}.20.0git DESTINATION ${OV_CPACK_RUNTIMEDIR} COMPONENT GcCpuRuntime) - endif () - - set(GRAPH_COMPILER_LIBS - GcInterface - MLIRLinalgx - ) - - if (ENABLE_INTEL_GPU) - list(APPEND GRAPH_COMPILER_LIBS GcGpuOclRuntime) + if (DEFINED GraphCompiler_DIR AND EXISTS "${GraphCompiler_DIR}/GraphCompilerTargets.cmake") + include("${GraphCompiler_DIR}/GraphCompilerTargets.cmake") + elseif (DEFINED GraphCompiler_ROOT) + include("${GraphCompiler_ROOT}/lib/cmake/GraphCompiler/GraphCompilerTargets.cmake") + else() + find_package(GraphCompiler REQUIRED) endif() + if(LLVM_DYLINK) + set(GRAPH_COMPILER_LIBS GcInterface GraphCompiler) + else() + set(GRAPH_COMPILER_LIBS GcInterface MLIRLinalgx GcGpuOclRuntime) + endif() set_property(GLOBAL PROPERTY GRAPH_COMPILER_LIBS ${GRAPH_COMPILER_LIBS}) endif () -if (NOT TARGET GcInterface) - if (DEFINED GraphCompiler_DIR AND EXISTS "${GraphCompiler_DIR}/GraphCompilerTargets.cmake") - include("${GraphCompiler_DIR}/GraphCompilerTargets.cmake") - elseif (DEFINED GraphCompiler_ROOT) - include("${GraphCompiler_ROOT}/lib/cmake/GraphCompiler/GraphCompilerTargets.cmake") - endif() -endif() - get_target_property(GRAPH_COMPILER_INCLUDES GcInterface INTERFACE_INCLUDE_DIRECTORIES) get_target_property(GRAPH_COMPILER_COMPILE_OPTIONS GcInterface INTERFACE_COMPILE_OPTIONS) diff --git a/src/cmake/openvino.cmake b/src/cmake/openvino.cmake index a89e228293b8c8..c2442718806de4 100644 --- a/src/cmake/openvino.cmake +++ b/src/cmake/openvino.cmake @@ -51,7 +51,11 @@ target_include_directories(${TARGET_NAME} INTERFACE find_package(MLIR REQUIRED CONFIG) -get_property(MLIR_ALL_LIBS GLOBAL PROPERTY MLIR_ALL_LIBS) +if (LLVM_DYLINK) + set(MLIR_ALL_LIBS LLVM MLIR) +else() + get_property(MLIR_ALL_LIBS GLOBAL PROPERTY MLIR_ALL_LIBS) +endif() target_link_libraries(${TARGET_NAME} PRIVATE openvino::reference From d1ebc65763be17980d17f1fb035ee769efa7baa5 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Tue, 17 Mar 2026 22:42:14 +0100 Subject: [PATCH 055/121] Update GC_BUILD.MD --- GC_BUILD.MD | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/GC_BUILD.MD b/GC_BUILD.MD index afac3d9376b555..284008f6277a12 100644 --- a/GC_BUILD.MD +++ b/GC_BUILD.MD @@ -28,9 +28,8 @@ export LLVM_INST_PATH=/home/jovyan/llvm/build/ ``` # clone -git clone https://github.com/intel/graph-compiler.git +git clone https://github.com/intel-sandbox/graph-compiler.git cd graph-compiler -git checkout AndreyPavlenko/ov mkdir build && cd build # install nanobind @@ -69,4 +68,7 @@ cmake --build . -j16 ``` OV_MLIR_DEBUG=1 OV_MLIR_MODE=GC ./bin/intel64/Release/ov_gpu_func_tests --gtest_filter=MLIRExecution.SimpleMatmulf32 -``` \ No newline at end of file +``` + +### CI runner +The CI runner is the X1 server - gcrun. When the session starts, it installs and starts itself automatically. The action scripts are located in the graph-compiler repository. From 92e9bbb2bdc83062def44e37ddcf0ec70f207259 Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Wed, 25 Mar 2026 18:02:35 +0100 Subject: [PATCH 056/121] Enable SDPA e2e (#62) Signed-off-by: dchigarev --- .../src/transformations/mlir/op/sdpa.cpp | 40 +- .../transformation_pipeline.cpp | 2 - .../src/plugin/transformations_pipeline.cpp | 2 +- .../tests/functional/mlir_op/sanity_tests.cpp | 190 +++++--- .../tests/functional/mlir_op/sdpa.cpp | 458 ++++++++++++++++++ 5 files changed, 620 insertions(+), 72 deletions(-) create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp diff --git a/src/common/transformations/src/transformations/mlir/op/sdpa.cpp b/src/common/transformations/src/transformations/mlir/op/sdpa.cpp index 0ed72604afb123..eaa4a30394ab71 100644 --- a/src/common/transformations/src/transformations/mlir/op/sdpa.cpp +++ b/src/common/transformations/src/transformations/mlir/op/sdpa.cpp @@ -6,8 +6,10 @@ #include "mlir/Dialect/Linalg/Passes.h" #include +#include #include "openvino/pass/pattern/op/wrap_type.hpp" #include +#include #include "mlir/Dialect/Arith/IR/Arith.h" #include "mlir/Dialect/Math/IR/Math.h" @@ -37,7 +39,7 @@ struct ConvertSDPA { auto sMap = AffineMap::get(/*dimCount=*/5, /*symbolCount=*/0, ctx); auto rMap = AffineMap::get(/*dimCount=*/5, /*symbolCount=*/0, {batch, m, n}, ctx); if (hasMask) { - auto mMap = AffineMap::get(/*dimCount=*/5, /*symbolCount=*/0, {batch, m, k2}, ctx); + auto mMap = AffineMap::get(/*dimCount=*/5, /*symbolCount=*/0, {m, k2}, ctx); return {qMap, kMap, vMap, sMap, mMap, rMap}; } return {qMap, kMap, vMap, sMap, rMap}; @@ -52,7 +54,7 @@ struct ConvertSDPA { auto sMap = AffineMap::get(/*dimCount=*/6, /*symbolCount=*/0, ctx); auto rMap = AffineMap::get(/*dimCount=*/6, /*symbolCount=*/0, {batch, head, m, n}, ctx); if (hasMask) { - auto mMap = AffineMap::get(/*dimCount=*/6, /*symbolCount=*/0, {batch, head, m, k2}, ctx); + auto mMap = AffineMap::get(/*dimCount=*/6, /*symbolCount=*/0, {m, k2}, ctx); return {qMap, kMap, vMap, sMap, mMap, rMap}; } return {qMap, kMap, vMap, sMap, rMap}; @@ -103,17 +105,41 @@ struct ConvertSDPA { if (input_size > 3 && !causal && node->get_input_partial_shape(3).rank().get_length() > 0) { mask = inputs[3]; hasMask = true; + + // Squeeze mask to 2D: mask can be 2D [M,N], 3D [1,M,N], or 4D [1,1,M,N] + // linalgx::AttentionOp expects a 2D mask [seq_q, seq_k] + auto maskShape = node->get_input_partial_shape(3); + auto maskRank = maskShape.rank().get_length(); + if (maskRank == 3) { + // [1, M, N] → [M, N]: collapse dims [0,1] and [2] + SmallVector reassoc = {{0, 1}, {2}}; + mask = builder.create(loc, mask, reassoc); + } else if (maskRank == 4) { + // [1, 1, M, N] → [M, N]: collapse dims [0,1,2] and [3] + SmallVector reassoc = {{0, 1, 2}, {3}}; + mask = builder.create(loc, mask, reassoc); + } + // maskRank == 2: already 2D, no change needed } // Extract or compute scale (input 4) Value scale; if (input_size > 4) { - // Scale is provided as input tensor - extract scalar from 0-dimensional tensor - // For a scalar tensor (shape {}), tensor.extract with no indices extracts the value - scale = builder.create(loc, inputs[4], mlir::ValueRange{}); + // Scale is provided as input tensor + // Check if it's an ov::Constant — if so, extract scalar value directly + auto scale_node = node->get_input_node_shared_ptr(4); + auto scale_const = std::dynamic_pointer_cast(scale_node); + if (scale_const) { + auto scale_val = scale_const->cast_vector()[0]; + scale = getConstant(builder, ov_output_element_type, scale_val); + } else { + OPENVINO_ASSERT(false, "SDPA: dynamic scale input is not supported in this version"); + } } else { - // Default scale: 1.0 - scale = getConstant(builder, ov_output_element_type, 1.0); + // Default scale: 1 / sqrt(head_dim), where head_dim is last dim of Q + auto head_dim = qShape[qRank - 1].get_length(); + float default_scale = 1.0f / std::sqrt(static_cast(head_dim)); + scale = getConstant(builder, ov_output_element_type, default_scale); } const auto ov_output_shape = node->get_output_partial_shape(0); diff --git a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp index c1e2e530984a84..4f13017ca5ed65 100644 --- a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp +++ b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp @@ -908,8 +908,6 @@ void Transformations::PreLpt(const std::vector& defaultPrecis CPU_REGISTER_PASS_COMMON(manager, ov::pass::ConstantFolding); CPU_REGISTER_PASS_COMMON(manager, ov::pass::LoraSubgraphFusion); - ov::pass::transformMLIR(model, std::make_shared()); - manager.run_passes(model); } diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index 72280e0003e4dc..de7732a7459443 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -698,7 +698,7 @@ void TransformationsPipeline::apply(std::shared_ptr func) { pass_config->set_callback([&](const std::shared_ptr node){ if (!config.get_enable_sdpa_optimization()) - return false; + return true; auto sdpa = ov::as_type_ptr(node); // TODO: sdpa_opt is not supporting sink_input for 1st token case yet diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp index 6b26a1b2e4464f..dc439bfbd70675 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp @@ -18,6 +18,8 @@ #include "openvino/core/partial_shape.hpp" #include "openvino/op/scaled_dot_product_attention.hpp" +#include + using testing::ElementsAreArray; static std::string model_full_path(const std::string& path) { @@ -170,6 +172,77 @@ static std::vector broadcast_vector(const std::vector& v, size_t new_size) return result; } +// Naive CPU implementation of Scaled Dot-Product Attention for reference results. +// Inputs are 3D: [batch, seq_len, head_dim] for Q/K/V. +// Computes: Output = softmax(Q * K^T * scale) * V +// All intermediate math is done in f32 for accuracy. +template +static std::vector sdpa_ref(const std::vector& Q, + const std::vector& K, + const std::vector& V, + size_t batch, size_t seq_q, size_t head_dim, + size_t seq_k, float scale) { + // Q: [batch, seq_q, head_dim] + // K: [batch, seq_k, head_dim] + // V: [batch, seq_k, head_dim] + // Output: [batch, seq_q, head_dim] + + std::vector output(batch * seq_q * head_dim); + + for (size_t b = 0; b < batch; ++b) { + const size_t q_offset = b * seq_q * head_dim; + const size_t k_offset = b * seq_k * head_dim; + const size_t v_offset = b * seq_k * head_dim; + const size_t o_offset = b * seq_q * head_dim; + + // Step 1: Compute S = Q * K^T * scale -> [seq_q, seq_k] + std::vector S(seq_q * seq_k, 0.0f); + for (size_t i = 0; i < seq_q; ++i) { + for (size_t j = 0; j < seq_k; ++j) { + float dot = 0.0f; + for (size_t d = 0; d < head_dim; ++d) { + dot += static_cast(Q[q_offset + i * head_dim + d]) + * static_cast(K[k_offset + j * head_dim + d]); + } + S[i * seq_k + j] = dot * scale; + } + } + + // Step 2: Row-wise softmax on S + for (size_t i = 0; i < seq_q; ++i) { + // Find row max for numerical stability + float row_max = S[i * seq_k]; + for (size_t j = 1; j < seq_k; ++j) { + row_max = std::max(row_max, S[i * seq_k + j]); + } + // Exponentiate and sum + float row_sum = 0.0f; + for (size_t j = 0; j < seq_k; ++j) { + S[i * seq_k + j] = std::exp(S[i * seq_k + j] - row_max); + row_sum += S[i * seq_k + j]; + } + // Normalize + for (size_t j = 0; j < seq_k; ++j) { + S[i * seq_k + j] /= row_sum; + } + } + + // Step 3: Output = S * V -> [seq_q, head_dim] + for (size_t i = 0; i < seq_q; ++i) { + for (size_t d = 0; d < head_dim; ++d) { + float acc = 0.0f; + for (size_t j = 0; j < seq_k; ++j) { + acc += S[i * seq_k + j] + * static_cast(V[v_offset + j * head_dim + d]); + } + output[o_offset + i * head_dim + d] = static_cast(acc); + } + } + } + + return output; +} + template static std::map allocate_input_tensors( ov::CompiledModel& compiledModel, @@ -195,7 +268,7 @@ static std::map allocate_input_tensors( return input_tensors; } -TEST(MLIRExecution, CompileSDPABasic) { +TEST(MLIRExecution, SDPABasic) { auto mode = ov::util::getenv_string("OV_MLIR_MODE"); if (mode != "GC_GPU" && mode != "GC") GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " @@ -222,74 +295,67 @@ TEST(MLIRExecution, CompileSDPABasic) { device_config[ov::intel_gpu::hint::enable_sdpa_optimization.name()] = false; auto compiled_model = core.compile_model(model, "GPU", device_config); -} - -TEST(MLIRExecution, CompileSDPA4DWithMaskAndScale) { - auto mode = ov::util::getenv_string("OV_MLIR_MODE"); - if (mode != "GC_GPU" && mode != "GC") - GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " - << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; - const ov::PartialShape query_shape{2, 8, 4096, 64}; - const ov::PartialShape key_shape{2, 8, 4096, 64}; - const ov::PartialShape value_shape{2, 8, 4096, 64}; - const ov::PartialShape mask_shape{2, 8, 4096, 4096}; - - const auto query = std::make_shared(ov::element::f16, query_shape); - const auto key = std::make_shared(ov::element::f16, key_shape); - const auto value = std::make_shared(ov::element::f16, value_shape); - const auto mask = std::make_shared(ov::element::f16, mask_shape); - const auto scale_const = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{0.125f}); - - const auto casual = false; - const auto sdpa = std::make_shared(query, - key, - value, - mask, - scale_const, - casual); - - auto model = std::make_shared(ov::OutputVector{sdpa}, ov::ParameterVector{query, key, value, mask}); - ov::Core core; - - ov::AnyMap device_config; - // disable sdpa-decomposition - device_config[ov::intel_gpu::hint::enable_sdpa_optimization.name()] = false; - auto compiled_model = core.compile_model(model, "GPU", device_config); -} + std::vector keep_alive; -TEST(MLIRExecution, CompileSDPAWithScaleNoMask) { - auto mode = ov::util::getenv_string("OV_MLIR_MODE"); - if (mode != "GC_GPU" && mode != "GC") - GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " - << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; - const ov::PartialShape query_shape{4, 4096, 64}; - const ov::PartialShape key_shape{4, 4096, 64}; - const ov::PartialShape value_shape{4, 4096, 64}; + // Fill Q, K, V with small random values in [-0.5, 0.5] to avoid f16 overflow + const size_t total = 4 * 4096 * 64; + std::mt19937 rng(42); // fixed seed for reproducibility + std::uniform_real_distribution dist(-0.5f, 0.5f); - const auto query = std::make_shared(ov::element::f16, query_shape); - const auto key = std::make_shared(ov::element::f16, key_shape); - const auto value = std::make_shared(ov::element::f16, value_shape); - // Empty mask constant (scalar placeholder) - const auto empty_mask = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{0.0f}); - const auto scale_const = ov::op::v0::Constant::create(ov::element::f16, ov::Shape{}, std::vector{0.125f}); + auto make_random_f16 = [&](size_t n) { + std::vector v(n); + for (auto& x : v) x = ov::float16(dist(rng)); + return v; + }; - const auto casual = false; - const auto sdpa = std::make_shared(query, - key, - value, - empty_mask, - scale_const, - casual); + std::vector matrix_q = make_random_f16(total); + std::vector matrix_k = make_random_f16(total); + std::vector matrix_v = make_random_f16(total); + std::map> input_values_map; + input_values_map.emplace(0, matrix_q); + input_values_map.emplace(1, matrix_k); + input_values_map.emplace(2, matrix_v); + auto input_tensors = allocate_input_tensors(compiled_model, input_values_map, true, keep_alive); - auto model = std::make_shared(ov::OutputVector{sdpa}, ov::ParameterVector{query, key, value}); - ov::Core core; + auto infer_req = compiled_model.create_infer_request(); + for (const auto& input : input_tensors) { + infer_req.set_input_tensor(input.first, input.second); + } + infer_req.infer(); - ov::AnyMap device_config; - // disable sdpa-decomposition - device_config[ov::intel_gpu::hint::enable_sdpa_optimization.name()] = false; + auto computed = infer_req.get_output_tensor(0); + ov::float16* result = reinterpret_cast(computed.data()); - auto compiled_model = core.compile_model(model, "GPU", device_config); + // Compute CPU reference: default scale = 1/sqrt(head_dim) = 1/sqrt(64) = 0.125 + auto reference = sdpa_ref(matrix_q, matrix_k, matrix_v, + /*batch=*/4, /*seq_q=*/4096, /*head_dim=*/64, + /*seq_k=*/4096, /*scale=*/0.125f); + + std::cout << "First 10 reference values: "; + for (size_t i = 0; i < 10; ++i) std::cout << reference[i] << " "; + std::cout << std::endl; + + std::cout << "First 10 result values: "; + for (size_t i = 0; i < 10; ++i) std::cout << result[i] << " "; + std::cout << std::endl; + + // Compare GPU result with the CPU reference using atol + rtol + // f16 SDPA chains matmul→softmax→matmul, so errors compound: + // rtol=1e-2 (f16 has ~3 decimal digits; two matmuls + exp compound) + // atol=1e-3 (handles values near zero where relative error blows up) + const float atol = 1e-3f; + const float rtol = 1e-2f; + for (size_t i = 0; i < reference.size(); ++i) { + float ref = static_cast(reference[i]); + float res = static_cast(result[i]); + float diff = std::abs(ref - res); + float tol = atol + rtol * std::abs(ref); + EXPECT_LE(diff, tol) + << "Mismatch at index " << i + << ": ref=" << ref << " res=" << res + << " diff=" << diff << " tol=" << tol; + } } TEST(MLIRExecution, SimpleMatmulf32) { diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp new file mode 100644 index 00000000000000..da0e0f858ac3bf --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp @@ -0,0 +1,458 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_test_utils/ov_tensor_utils.hpp" +#include "common_test_utils/test_enums.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" +#include "openvino/opsets/opset13_decl.hpp" +#include "transformations/op_conversions/scaled_dot_product_attention_decomposition.hpp" +#include "openvino/pass/manager.hpp" + +#include "openvino/op/parameter.hpp" +#include "openvino/op/constant.hpp" +#include "openvino/op/result.hpp" +#include "openvino/op/matmul.hpp" + +#include "intel_gpu/runtime/execution_config.hpp" +#include "openvino/op/transpose.hpp" + +namespace { +using ov::test::InputShape; + +typedef std::tuple, // shape + bool, // is_causal + bool, // has_attn + bool, // is_attn_const + bool, // has_scale + bool, // is_scale_const + std::vector>, // input_transpose + bool // has_sink + > ScaledAttnGPUTestParams; + +class ScaledAttnLayerGPUMlirTest : public testing::WithParamInterface, + virtual public ov::test::SubgraphBaseTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj); + +protected: + void SetUp() override; + void generate_inputs(const std::vector& targetInputStaticShapes) override; + void transpose_prepare(std::vector& shapes, const std::vector>& input_transpose); + bool is_causal; + bool has_attn; + bool is_attn_const; + bool has_scale; + bool is_scale_const; + bool has_sink; +}; + +std::string ScaledAttnLayerGPUMlirTest::getTestCaseName(const testing::TestParamInfo& obj) { + bool transpose_enable; + const auto& [inType, inputShapes, is_causal, has_attn, is_attn_const, has_scale, is_scale_const, input_transpose, has_sink] = obj.param; + + transpose_enable = (input_transpose.size() != 0); + std::ostringstream result; + result << "netPRC=" << inType << "_"; + result << "IS="; + for (const auto& inputShape : inputShapes) { + result << ov::test::utils::partialShape2str({inputShape.first}) << "_"; + } + result << "TS="; + for (const auto& shapes : inputShapes) { + for (const auto& shape : shapes.second) { + result << ov::test::utils::vec2str(shape); + result << "_"; + } + } + result << "is_causal=" << is_causal << "_"; + result << "has_attn=" << has_attn << "_"; + result << "is_attn_const=" << is_attn_const << "_"; + result << "has_scale=" << has_scale << "_"; + result << "is_scale_const=" << is_scale_const << "_"; + result << "with_transpose" << transpose_enable << "_"; + result << "has_sink=" << has_sink << "_"; + + return result.str(); +} + +void ScaledAttnLayerGPUMlirTest::SetUp() { + targetDevice = ov::test::utils::DEVICE_GPU; + configuration[ov::intel_gpu::hint::enable_sdpa_optimization.name()] = false; + + const auto& [inType, _inputShapes, _is_causal, _has_attn, _is_attn_const, _has_scale, _is_scale_const, input_transpose, _has_sink] = this->GetParam(); + is_causal = _is_causal; + has_attn = _has_attn; + is_attn_const = _is_attn_const; + has_scale = _has_scale; + is_scale_const = _is_scale_const; + auto inputShapes = _inputShapes; + has_sink = _has_sink; + + transpose_prepare(inputShapes, input_transpose); + init_input_shapes(inputShapes); + + ov::ParameterVector inputParams; + // q, k, v + inputParams.push_back(std::make_shared(inType, inputDynamicShapes[0])); + inputParams.push_back(std::make_shared(inType, inputDynamicShapes[1])); + inputParams.push_back(std::make_shared(inType, inputDynamicShapes[2])); + inputParams[0]->set_friendly_name("q"); + inputParams[1]->set_friendly_name("k"); + inputParams[2]->set_friendly_name("v"); + if (!has_attn && has_scale) { + inputParams.push_back(std::make_shared(inType, ov::PartialShape{})); + inputParams.back()->set_friendly_name("attention_mask"); + if (!is_scale_const) { + inputParams.push_back(std::make_shared(inType, ov::PartialShape{1})); + inputParams.back()->set_friendly_name("scale"); + } + } else { + if (has_attn && !is_attn_const) { + inputParams.push_back(std::make_shared(inType, inputDynamicShapes[3])); + inputParams.back()->set_friendly_name("attention_mask"); + if (has_scale && !is_scale_const) { + inputParams.push_back(std::make_shared(inType, ov::PartialShape{1})); + inputParams.back()->set_friendly_name("scale"); + } + } + } + + ov::OutputVector inputParams_transpose; + for (size_t i = 0; i < inputParams.size(); i++) { + inputParams_transpose.push_back(inputParams[i]); + } + if (has_attn && is_attn_const) { + auto attn_const = std::make_shared(inType, ov::Shape{}, 0.0f); + attn_const->set_friendly_name("attention_mask"); + inputParams_transpose.push_back(attn_const); + if (has_scale && !is_scale_const) { + auto scale_param = std::make_shared(inType, ov::PartialShape{1}); + scale_param->set_friendly_name("scale"); + inputParams.push_back(scale_param); + inputParams_transpose.push_back(scale_param); + } + } else if (has_sink && !has_attn) { + // Add default mask when sink token exists and attention mask is not present + auto attn_const = std::make_shared(inType, ov::Shape{}, 0.0f); + attn_const->set_friendly_name("attention_mask"); + inputParams_transpose.push_back(attn_const); + } + if (has_scale && is_scale_const) { + auto scale_const = std::make_shared(inType, ov::Shape({1}), 0.35f); + scale_const->set_friendly_name("scale"); + inputParams_transpose.push_back(scale_const); + } else if (has_sink && !has_scale) { + // Add default scale when sink token exists and scale is not present + auto scale_const = std::make_shared(inType, ov::Shape({1}), 0.35f); + scale_const->set_friendly_name("scale"); + inputParams_transpose.push_back(scale_const); + } + + if (input_transpose.size() != 0) { + auto rank = input_transpose[0].size(); + // deal with transpose. + auto tranpose_a_const = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{rank}, input_transpose[0]); + auto tranpose_a = std::make_shared(inputParams[0], tranpose_a_const); + tranpose_a->set_friendly_name("tranpose_a"); + inputParams_transpose[0] = tranpose_a; + + auto tranpose_b_const = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{rank}, input_transpose[1]); + auto tranpose_b = std::make_shared(inputParams[1], tranpose_b_const); + tranpose_b->set_friendly_name("tranpose_b"); + inputParams_transpose[1] = tranpose_b; + + auto tranpose_c_const = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{rank}, input_transpose[2]); + auto tranpose_c = std::make_shared(inputParams[2], tranpose_c_const); + tranpose_c->set_friendly_name("tranpose_c"); + inputParams_transpose[2] = tranpose_c; + } + + ov::OutputVector inputs; + for (size_t i = 0; i < inputParams_transpose.size(); i++) { + inputs.push_back(inputParams_transpose[i]); + } + if (has_sink) { + size_t num_heads = inputDynamicShapes[0][1].get_length(); + ov::test::utils::InputGenerateData data(0, 5, 100); + auto sink_tensor = ov::test::utils::create_and_fill_tensor(ov::element::f16, ov::Shape{1, num_heads, 1, 1}, data); + auto sink_const = std::make_shared(sink_tensor); + sink_const->set_friendly_name("sink"); + inputs.push_back(sink_const); + } + auto sdp = std::make_shared(inputs, is_causal); + sdp->set_friendly_name("sdpa"); + + auto output = std::make_shared(sdp->output(0)); + + function = std::make_shared(ov::OutputVector{output}, inputParams, "sdpa_model"); + + functionRefs = function->clone(); + + // Set friendly name on the SDPA node in the reference model + for (const auto& node : functionRefs->get_ops()) { + if (ov::as_type_ptr(node)) { + node->set_friendly_name("decompose_me_sdpa"); + break; + } + } + + ov::pass::Manager manager; + + // Decompose ScaledDotProductAttention + manager.register_pass(); + manager.run_passes(functionRefs); + + auto it = std::find_if(inputShapes[1].second.begin(), inputShapes[1].second.end(), [&](const ov::Shape& shape){ + return shape[0] >= 128 || shape[2] >= 384 || shape[3] >= 128; + }); + + bool has_diff_head_size = inputShapes[1].first.begin()[3] != inputShapes[2].first.begin()[3]; + + bool has_long_seq = it != inputShapes[1].second.end(); + + if (inType == ov::element::f16) { + if (has_sink || (has_diff_head_size && !has_scale)) { + abs_threshold = 0.1; + rel_threshold = 0.1; + } else if (has_long_seq) { + abs_threshold = 0.025; + rel_threshold = 0.025; + } else { + abs_threshold = 0.005; + rel_threshold = 0.005; + } + } +} + +void ScaledAttnLayerGPUMlirTest::transpose_prepare(std::vector& shapes, + const std::vector>& input_transpose) { + auto transpose_pshape = [](InputShape& pshapes, const std::vector& order) { + auto transposed_pshape = ov::PartialShape::dynamic(pshapes.first.rank()); + std::vector transposed_cshapes(pshapes.second); + auto& pshape = pshapes.first; + auto& cshape = pshapes.second; + for (size_t i = 0; i < order.size(); i++) { + transposed_pshape[i] = pshape[order[i]]; + for (size_t j = 0; j < cshape.size(); j++) { + transposed_cshapes[j][i] = cshape[j][order[i]]; + } + } + + for (size_t i = 0; i < order.size(); i++) { + pshape[i] = transposed_pshape[i]; + for (size_t j = 0; j < cshape.size(); j++) { + cshape[j][i] = transposed_cshapes[j][i]; + } + } + }; + + if (shapes.empty()) { + return; + } + + if (input_transpose.empty()) { + return; + } + + for (size_t i = 0; i < input_transpose.size(); i++) { + transpose_pshape(shapes[i], input_transpose[i]); + } +} + +void ScaledAttnLayerGPUMlirTest::generate_inputs(const std::vector& targetInputStaticShapes) { + const auto& model_inputs = function->inputs(); + inputs.clear(); + std::vector shapes(3); + { + for (int i = 0; i < 3; ++i) { + shapes[i] = targetInputStaticShapes[i]; + ov::test::utils::InputGenerateData data(-1, 1, 64); + ov::Tensor data_tensor = ov::test::utils::create_and_fill_tensor(ov::element::f16, shapes[i], data); + inputs.insert({model_inputs[i].get_node_shared_ptr(), data_tensor}); + } + } + ov::test::utils::InputGenerateData attn_data(-1.0f, 2, 1); + ov::test::utils::InputGenerateData scale_data(0.1f, 1, 10); + if (!has_attn && has_scale) { + shapes.push_back(ov::Shape{}); + ov::Tensor attn_tensor = ov::test::utils::create_and_fill_tensor(ov::element::f16, shapes[3], attn_data); + inputs.insert({model_inputs[3].get_node_shared_ptr(), attn_tensor}); + if (!is_scale_const) { + shapes.push_back(ov::Shape{1}); + ov::Tensor scale_tensor = ov::test::utils::create_and_fill_tensor(ov::element::f16, shapes[4], scale_data); + inputs.insert({model_inputs[4].get_node_shared_ptr(), scale_tensor}); + } + } else { + int idx = 3; + if (has_attn && !is_attn_const) { + shapes.push_back(targetInputStaticShapes[3]); + ov::Tensor attn_tensor = ov::test::utils::create_and_fill_tensor(ov::element::f16, shapes[idx], attn_data); + inputs.insert({model_inputs[idx++].get_node_shared_ptr(), attn_tensor}); + } + if (has_scale && !is_scale_const) { + shapes.push_back(ov::Shape{1}); + ov::Tensor scale_tensor = ov::test::utils::create_and_fill_tensor(ov::element::f16, shapes[idx], scale_data); + inputs.insert({model_inputs[idx].get_node_shared_ptr(), scale_tensor}); + } + } + + // Print first 10 values of each input + for (const auto& [node, tensor] : inputs) { + size_t n = std::min(tensor.get_size(), 10); + std::cout << "Input \"" << node->get_friendly_name() << "\" shape=" << tensor.get_shape() + << " type=" << tensor.get_element_type() << " first " << n << " values: ["; + if (tensor.get_element_type() == ov::element::f16) { + auto* data = reinterpret_cast(tensor.data()); + for (size_t i = 0; i < n; ++i) { + if (i > 0) std::cout << ", "; + std::cout << static_cast(data[i]); + } + } else if (tensor.get_element_type() == ov::element::f32) { + auto* data = reinterpret_cast(tensor.data()); + for (size_t i = 0; i < n; ++i) { + if (i > 0) std::cout << ", "; + std::cout << data[i]; + } + } + std::cout << "]" << std::endl; + } +} + +TEST_P(ScaledAttnLayerGPUMlirTest, CompareWithRefs) { + run(); +} + +const std::vector> disable_transpose{}; +const std::vector> static_shapes_3D{ + // static shapes + { + // q shape + {ov::test::InputShape{ov::PartialShape{16, 128, 80}, + {ov::Shape{16, 128, 80}}} + }, + // k shape + {ov::test::InputShape{ov::PartialShape{16, 128, 80}, + {ov::Shape{16, 128, 80}}} + }, + // v shape + {ov::test::InputShape{ov::PartialShape{16, 128, 80}, + {ov::Shape{16, 128, 80}}} + }, + // attn shape: [B, 128, -128, L0+L1] + {ov::test::InputShape{ov::PartialShape{128, 128}, + {ov::Shape{128, 128}}} + }, + }, +}; + +const auto static_shape_params_3D = testing::Combine(testing::Values(ov::element::f16), + testing::ValuesIn(static_shapes_3D), + testing::Values(false), // is_causal + testing::Values(false, true), // has_attn + testing::Values(false, true), // is_attn_const + testing::Values(false, true), // has_scale + testing::Values(/*false,*/ true), // is_scale_const + testing::ValuesIn({disable_transpose}), + testing::Values(false)); // has_sink + +INSTANTIATE_TEST_SUITE_P(smoke_ScaledAttnStatic3D_GPU, + ScaledAttnLayerGPUMlirTest, + static_shape_params_3D, + ScaledAttnLayerGPUMlirTest::getTestCaseName); + +const std::vector> static_shapes_3D_4_4096_64{ + { + // q shape + {ov::test::InputShape{ov::PartialShape{4, 4096, 64}, + {ov::Shape{4, 4096, 64}}} + }, + // k shape + {ov::test::InputShape{ov::PartialShape{4, 4096, 64}, + {ov::Shape{4, 4096, 64}}} + }, + // v shape + {ov::test::InputShape{ov::PartialShape{4, 4096, 64}, + {ov::Shape{4, 4096, 64}}} + }, + // attn shape (unused when has_attn=false) + {ov::test::InputShape{ov::PartialShape{4096, 4096}, + {ov::Shape{4096, 4096}}} + }, + }, +}; + +const auto static_shape_params_3D_4_4096_64 = testing::Combine( + testing::Values(ov::element::f16), + testing::ValuesIn(static_shapes_3D_4_4096_64), + testing::Values(false), // is_causal + testing::Values(false), // has_attn + testing::Values(false), // is_attn_const + testing::Values(false), // has_scale + testing::Values(false), // is_scale_const + testing::Values(disable_transpose), + testing::Values(false)); // has_sink + +INSTANTIATE_TEST_SUITE_P(smoke_ScaledAttnStatic3D_4_4096_64_GPU, + ScaledAttnLayerGPUMlirTest, + static_shape_params_3D_4_4096_64, + ScaledAttnLayerGPUMlirTest::getTestCaseName); + + +const std::vector> static_shapes_4D{ + // static shapes + { + // q shape + {ov::test::InputShape{ov::PartialShape{1, 16, 128, 80}, + {ov::Shape{1, 16, 128, 80}}} + }, + // k shape + {ov::test::InputShape{ov::PartialShape{1, 16, 128, 80}, + {ov::Shape{1, 16, 128, 80}}} + }, + // v shape + {ov::test::InputShape{ov::PartialShape{1, 16, 128, 80}, + {ov::Shape{1, 16, 128, 80}}} + }, + // attn shape: [B, 128, -128, L0+L1] + {ov::test::InputShape{ov::PartialShape{128, 128}, + {ov::Shape{128, 128}}} + }, + }, + { + // q shape + {ov::test::InputShape{ov::PartialShape{1, 8, 128, 128}, + {ov::Shape{1, 8, 128, 128}}} + }, + // k shape + {ov::test::InputShape{ov::PartialShape{1, 8, 128, 128}, + {ov::Shape{1, 8, 128, 128}}} + }, + // v shape + {ov::test::InputShape{ov::PartialShape{1, 8, 128, 128}, + {ov::Shape{1, 8, 128, 128}}} + }, + // attn shape: [B, 1, -1, L0+L1] + {ov::test::InputShape{ov::PartialShape{1, 1, 128, 128}, + {ov::Shape{1, 1, 128, 128}}} + }, + }, +}; + +const auto static_shape_params_4D = testing::Combine(testing::Values(ov::element::f16), + testing::ValuesIn(static_shapes_4D), + testing::Values(false), // is_causal + testing::Values(false, true), // has_attn + testing::Values(false, true), // is_attn_const + testing::Values(false, true), // has_scale + testing::Values(/*false,*/true), // is_scale_const + testing::ValuesIn({disable_transpose}), + testing::Values(false)); // has_sink + +INSTANTIATE_TEST_SUITE_P(smoke_ScaledAttnStatic4D_GPU, + ScaledAttnLayerGPUMlirTest, + static_shape_params_4D, + ScaledAttnLayerGPUMlirTest::getTestCaseName); + +} // namespace From 27f8b35cca885058b384990daaf0d42ff3a9f9ef Mon Sep 17 00:00:00 2001 From: dchigarev Date: Wed, 25 Mar 2026 18:27:09 +0000 Subject: [PATCH 057/121] Optimize SDPA tests Signed-off-by: dchigarev --- .../tests/functional/mlir_op/sanity_tests.cpp | 4 +- .../tests/functional/mlir_op/sdpa.cpp | 38 ------------------- 2 files changed, 2 insertions(+), 40 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp index dc439bfbd70675..91760642eac9fb 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp @@ -292,7 +292,7 @@ TEST(MLIRExecution, SDPABasic) { ov::AnyMap device_config; // disable sdpa-decomposition - device_config[ov::intel_gpu::hint::enable_sdpa_optimization.name()] = false; + device_config[ov::intel_gpu::hint::enable_sdpa_optimization.name()] = true; auto compiled_model = core.compile_model(model, "GPU", device_config); @@ -301,7 +301,7 @@ TEST(MLIRExecution, SDPABasic) { // Fill Q, K, V with small random values in [-0.5, 0.5] to avoid f16 overflow const size_t total = 4 * 4096 * 64; std::mt19937 rng(42); // fixed seed for reproducibility - std::uniform_real_distribution dist(-0.5f, 0.5f); + std::uniform_real_distribution dist(-1.0f, 1.0f); auto make_random_f16 = [&](size_t n) { std::vector v(n); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp index da0e0f858ac3bf..f7bb0012dbe011 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp @@ -362,44 +362,6 @@ INSTANTIATE_TEST_SUITE_P(smoke_ScaledAttnStatic3D_GPU, static_shape_params_3D, ScaledAttnLayerGPUMlirTest::getTestCaseName); -const std::vector> static_shapes_3D_4_4096_64{ - { - // q shape - {ov::test::InputShape{ov::PartialShape{4, 4096, 64}, - {ov::Shape{4, 4096, 64}}} - }, - // k shape - {ov::test::InputShape{ov::PartialShape{4, 4096, 64}, - {ov::Shape{4, 4096, 64}}} - }, - // v shape - {ov::test::InputShape{ov::PartialShape{4, 4096, 64}, - {ov::Shape{4, 4096, 64}}} - }, - // attn shape (unused when has_attn=false) - {ov::test::InputShape{ov::PartialShape{4096, 4096}, - {ov::Shape{4096, 4096}}} - }, - }, -}; - -const auto static_shape_params_3D_4_4096_64 = testing::Combine( - testing::Values(ov::element::f16), - testing::ValuesIn(static_shapes_3D_4_4096_64), - testing::Values(false), // is_causal - testing::Values(false), // has_attn - testing::Values(false), // is_attn_const - testing::Values(false), // has_scale - testing::Values(false), // is_scale_const - testing::Values(disable_transpose), - testing::Values(false)); // has_sink - -INSTANTIATE_TEST_SUITE_P(smoke_ScaledAttnStatic3D_4_4096_64_GPU, - ScaledAttnLayerGPUMlirTest, - static_shape_params_3D_4_4096_64, - ScaledAttnLayerGPUMlirTest::getTestCaseName); - - const std::vector> static_shapes_4D{ // static shapes { From 058ee2a4d40154852048a541249c49b4c0c6e127 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Fri, 27 Mar 2026 16:22:20 +0000 Subject: [PATCH 058/121] Update build instructions Signed-off-by: dchigarev --- GC_BUILD.MD | 60 ++++++++++++++++++----------------------------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/GC_BUILD.MD b/GC_BUILD.MD index 284008f6277a12..694a37043aee60 100644 --- a/GC_BUILD.MD +++ b/GC_BUILD.MD @@ -1,47 +1,22 @@ ### Tested on x1-spr 'Triton (GPU, Agama 2521.10, DLE 2025.1.1, Ubuntu 22.04)' profile -#### step 1: build llvm (tested on 2634a2bda1db92ab5324a47459ee7f23e531ce53) -Important! RTTI has to be enabled! -``` -cmake -G Ninja ../llvm \ - -DLLVM_ENABLE_DUMP=1 \ - -DCMAKE_BUILD_TYPE=Release \ - -DLLVM_ENABLE_ASSERTIONS=true \ - -DLLVM_ENABLE_PROJECTS="mlir;lld" \ - -DLLVM_TARGETS_TO_BUILD="X86;SPIRV" \ - -DLLVM_INSTALL_UTILS=true \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -DLLVM_ENABLE_RTTI=ON \ - -DLLVM_ENABLE_EH=ON \ - -DCMAKE_INSTALL_PREFIX=/home/jovyan/llvm/curr_install - -cmake --build . --target install -``` +If you feel that these steps look outdated, check the actual CI-steps here: https://github.com/intel-sandbox/graph-compiler/blob/main/.github/workflows/ci.yml -#### step 2: install opencl-headers & install llvm-env-vars +#### step 1: install opencl-headers & install llvm-env-vars ``` sudo apt install -y intel-opencl-icd opencl-c-headers ocl-icd-opencl-dev -export LLVM_INST_PATH=/home/jovyan/llvm/build/ ``` -#### step 3: build graph-compiler +#### step 2: build graph-compiler + llvm ``` # clone git clone https://github.com/intel-sandbox/graph-compiler.git cd graph-compiler -mkdir build && cd build - # install nanobind pip install nanobind -# build -cmake ../ -G Ninja \ - -DLLVM_DIR=$LLVM_INST_PATH/lib/cmake/llvm \ - -DMLIR_DIR=$LLVM_INST_PATH/lib/cmake/mlir \ - -DCMAKE_INSTALL_PREFIX=/home/jovyan/graph-compiler/install - -cmake --build . --target install +./scripts/compile.sh -l ``` #### step 4: build openvino @@ -52,22 +27,27 @@ cd openvino git checkout mlir-gc-integration mkdir build && cd build -cmake ../ -G Ninja -DLLVM_DIR=$LLVM_INST_PATH/lib/cmake/llvm \ - -DMLIR_DIR=$LLVM_INST_PATH/lib/cmake/mlir \ - -DENABLE_GRAPH_COMPILER=ON \ - -DENABLE_INTEL_GPU=ON \ - -DENABLE_TESTS=ON \ - -DENABLE_ONEDNN_FOR_GPU=OFF \ - -DENABLE_INTEL_CPU=OFF \ - -DCMAKE_CXX_FLAGS="-DOV_GPU_OPENCL_HPP_HAS_UUID -DOV_GPU_OPENCL_HPP_HAS_BUS_INFO" \ - -DGraphCompiler_DIR=/home/jovyan/graph-compiler/install/lib/cmake/GraphCompiler -cmake --build . -j16 +cmake -G Ninja -S "$ov_dir" -B "$ov_dir/build" \ + -DLLVM_DIR=$llvm_dir/lib/cmake/llvm \ + -DMLIR_DIR=$llvm_dir/lib/cmake/mlir \ + -DENABLE_GRAPH_COMPILER=ON \ + -DLLVM_DYLINK=ON \ + -DENABLE_INTEL_GPU=ON \ + -DENABLE_TESTS=ON \ + -DENABLE_ONEDNN_FOR_GPU=OFF \ + -DENABLE_INTEL_CPU=OFF \ + -DENABLE_INTEL_NPU=OFF \ + -DCMAKE_CXX_FLAGS="-DOV_GPU_OPENCL_HPP_HAS_UUID -DOV_GPU_OPENCL_HPP_HAS_BUS_INFO" \ + -DGraphCompiler_DIR=$gc_dir/lib/cmake/GraphCompiler \ + -DOpenCL_HPP_INCLUDE_DIR="$ov_dir/thirdparty/ocl/clhpp_headers/include" \ + -DOpenCL_HPP="$ov_dir/thirdparty/ocl/clhpp_headers/include/CL/opencl.hpp" +cmake --build "$ov_dir/build" -j16 ``` #### step 5: run sanity-mlir tests and dump mlir ``` -OV_MLIR_DEBUG=1 OV_MLIR_MODE=GC ./bin/intel64/Release/ov_gpu_func_tests --gtest_filter=MLIRExecution.SimpleMatmulf32 +OV_MLIR_DEBUG=1 OV_MLIR_MODE=GC_GPU ./bin/intel64/Release/ov_gpu_func_tests --gtest_filter=MLIRExecution.SimpleMatmulf16 ``` ### CI runner From 992694e622b1536f2e5a5897482b7fdd57a0a687 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Thu, 2 Apr 2026 14:51:32 +0000 Subject: [PATCH 059/121] Implemented reduction converter --- .../src/transformations/mlir/convert.cpp | 11 + .../transformations/mlir/convert_common.hpp | 22 +- .../src/transformations/mlir/op/reduce.cpp | 193 ++++++++++++++++++ .../src/transformations/mlir/op/reduce.hpp | 20 ++ 4 files changed, 239 insertions(+), 7 deletions(-) create mode 100644 src/common/transformations/src/transformations/mlir/op/reduce.cpp create mode 100644 src/common/transformations/src/transformations/mlir/op/reduce.hpp diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index 52a18d8407b590..a3a5745c305ed5 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -10,6 +10,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include @@ -85,6 +90,7 @@ #include "op/unsqueeze.hpp" #include "op/sdpa.hpp" #include "op/binary_eltwise.hpp" +#include "op/reduce.hpp" #include "openvino/core/dimension.hpp" #include "openvino/core/rt_info.hpp" #include "openvino/core/symbol.hpp" @@ -326,6 +332,11 @@ void injectMLIR(std::shared_ptr model, manager.register_pass>(); manager.register_pass>(); manager.register_pass>(); + manager.register_pass>(); + manager.register_pass>(); + manager.register_pass>(); + manager.register_pass>(); + manager.register_pass>(); manager.register_pass(); manager.register_pass(); manager.register_pass(); diff --git a/src/common/transformations/src/transformations/mlir/convert_common.hpp b/src/common/transformations/src/transformations/mlir/convert_common.hpp index 5b6b83ba6d3d5f..43da4220ef0d0d 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.hpp +++ b/src/common/transformations/src/transformations/mlir/convert_common.hpp @@ -40,19 +40,27 @@ Location createLocation(MLIRContext* ctx, NodePtr node); bool elementwise_no_broadcast_predicate(const ov::Output& output); -// Borrowed it from TPP-MLIR. FIXME: Do we have a better upstreamed alternative? template -mlir::arith::ConstantOp getConstant(OpBuilder &builder, const ov::element::Type& precision, T value) { - auto unkLoc = builder.getUnknownLoc(); +mlir::arith::ConstantOp getConstant(OpBuilder& builder, + const mlir::Type& type, + T value, + std::optional loc = std::nullopt) { TypedAttr attr; - auto type = importPrecision(builder.getContext(), precision); - if(precision.is_integral()) { + if (type.isInteger()) { attr = builder.getIntegerAttr(type, int64_t(value)); - } else if(precision.is_real()) { + } else if (type.isFloat()) { attr = builder.getFloatAttr(type, double(value)); } assert(attr && "Unsupported ConstantOp type"); - return builder.create(unkLoc, type, attr); + return arith::ConstantOp::create(builder, loc.value_or(builder.getUnknownLoc()), type, attr); +} + +template +mlir::arith::ConstantOp getConstant(OpBuilder& builder, + const ov::element::Type& precision, + T value, + std::optional loc = std::nullopt) { + return getConstant(builder, importPrecision(builder.getContext(), precision), value, loc); } bool has_dynamic_rank(NodePtr node); diff --git a/src/common/transformations/src/transformations/mlir/op/reduce.cpp b/src/common/transformations/src/transformations/mlir/op/reduce.cpp new file mode 100644 index 00000000000000..3458e2daff06c7 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/reduce.cpp @@ -0,0 +1,193 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "reduce.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "../convert_common.hpp" +#include "mlir/Dialect/Arith/IR/Arith.h" +#include "mlir/Dialect/Linalg/IR/Linalg.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" +#include "openvino/pass/pattern/op/wrap_type.hpp" + +namespace { + +using namespace ov; +using namespace ov::mlir; +using Value = ::mlir::Value; +using ValueRange = ::mlir::ValueRange; + +template +struct ConvertReduce { + ConvertReduce() = default; + + void operator()(ConversionContext& context, NodePtr node) { + auto el_type = importPrecision(context.context, node->get_input_element_type(0)); + auto input_shape = node->get_input_partial_shape(0); + auto input_rank = input_shape.rank().get_length(); + SmallVector reduction_axes; + { + auto input1 = dynamic_cast(node->get_input_node_ptr(1)); + assert(input1 && "Only constant axes are supported"); + auto axes = input1->cast_vector(); + reduction_axes.reserve(axes.size()); + for (int64_t axis : axes) { + reduction_axes.push_back(axis < 0 ? axis + input_rank : axis); + } + } + ::mlir::RankedTensorType result_type; + { + SmallVector shape; + for (size_t i = 0; i < input_rank; ++i) { + if (!llvm::is_contained(reduction_axes, i)) { + auto dim = input_shape[i]; + assert(dim.is_static() && "Dynamic shapes not supported"); + shape.push_back(dim.get_length()); + } + } + result_type = RankedTensorType::get(shape, el_type); + } + + auto& builder = context.builder(); + auto loc = createLocation(context.context, node); + auto empty = ::mlir::tensor::EmptyOp::create(builder, loc, result_type, ValueRange{}); + Value init_value = create_init_value(builder, loc, el_type); + auto output = ::mlir::linalg::FillOp::create(builder, loc, ValueRange{init_value}, ValueRange{empty}); + Value result = ::mlir::linalg::ReduceOp::create( + builder, + loc, + ValueRange{context.getInputs(node)[0]}, + ValueRange{output.getResult(0)}, + reduction_axes, + [&](::mlir::OpBuilder& b, ::mlir::Location loc, ValueRange inputs) { + Value result = create_payload_op(b, loc, inputs[0], inputs[1], el_type); + ::mlir::linalg::YieldOp::create(b, loc, result); + }) + .getResult(0); + + // For ReduceMean, divide by the number of elements + if constexpr (std::is_same_v) { + // Calculate the number of elements in the reduction dimensions + int64_t num_els = 1; + for (auto axis : reduction_axes) { + num_els *= input_shape[axis].get_length(); + } + ::mlir::TypedAttr divisor_attr; + if (el_type.isInteger()) { + divisor_attr = builder.getIntegerAttr(el_type, num_els); + } else { + divisor_attr = builder.getFloatAttr(el_type, static_cast(num_els)); + } + auto divisor = ::mlir::arith::ConstantOp::create(builder, + loc, + ::mlir::DenseElementsAttr::get(result_type, divisor_attr)); + result = ::mlir::linalg::DivOp::create(builder, loc, ValueRange{result, divisor}, ValueRange{result}) + .getResult(0); + } + + // If keep_dims is true, broadcast along the reduced dimensions + if (auto keep_dims = dynamic_cast(node.get()); + keep_dims && keep_dims->get_keep_dims()) { + auto shape = llvm::map_to_vector(node->get_output_partial_shape(0), [](const ov::Dimension& dim) { + return dim.get_length(); + }); + auto empty = ::mlir::tensor::EmptyOp::create(builder, loc, shape, el_type); + result = ::mlir::linalg::BroadcastOp::create(builder, loc, result, empty, reduction_axes).getResult()[0]; + } + + context.nodeOutputMap[node->output(0)] = result; + } + +private: + Value create_init_value(::mlir::OpBuilder& builder, ::mlir::Location loc, ::mlir::Type type) { + if constexpr (std::is_same_v) { + if (type.isFloat()) { + return getConstant(builder, type, -std::numeric_limits::infinity(), loc); + } else { + int64_t min_val = type.isUnsignedInteger() ? 0 : -(1LL << (type.getIntOrFloatBitWidth() - 1)); + return getConstant(builder, type, min_val, loc); + } + } else if constexpr (std::is_same_v) { + if (type.isFloat()) { + return getConstant(builder, type, std::numeric_limits::infinity(), loc); + } else { + unsigned bitwidth = type.getIntOrFloatBitWidth(); + int64_t max_val = type.isUnsignedInteger() ? ((1ULL << bitwidth) - 1) : ((1LL << (bitwidth - 1)) - 1); + return getConstant(builder, type, max_val, loc); + } + } else if constexpr (std::is_same_v || + std::is_same_v) { + return getConstant(builder, type, 0, loc); + } else if constexpr (std::is_same_v) { + return getConstant(builder, type, 1, loc); + } else { + static_assert(false, "Unsupported reduction operation"); + } + } + + Value create_payload_op(::mlir::OpBuilder& builder, ::mlir::Location loc, Value lhs, Value rhs, ::mlir::Type type) { + if constexpr (std::is_same_v) { + if (type.isFloat()) { + return ::mlir::arith::MaximumFOp::create(builder, loc, lhs, rhs); + } else if (type.isUnsignedInteger()) { + return ::mlir::arith::MaxUIOp::create(builder, loc, lhs, rhs); + } else { + return ::mlir::arith::MaxSIOp::create(builder, loc, lhs, rhs); + } + } else if constexpr (std::is_same_v) { + if (type.isFloat()) { + return ::mlir::arith::MinimumFOp::create(builder, loc, lhs, rhs); + } else if (type.isUnsignedInteger()) { + return ::mlir::arith::MinUIOp::create(builder, loc, lhs, rhs); + } else { + return ::mlir::arith::MinSIOp::create(builder, loc, lhs, rhs); + } + } else if constexpr (std::is_same_v || + std::is_same_v) { + if (type.isFloat()) { + return ::mlir::arith::AddFOp::create(builder, loc, lhs, rhs); + } else { + return ::mlir::arith::AddIOp::create(builder, loc, lhs, rhs); + } + } else if constexpr (std::is_same_v) { + if (type.isFloat()) { + return ::mlir::arith::MulFOp::create(builder, loc, lhs, rhs); + } else { + return ::mlir::arith::MulIOp::create(builder, loc, lhs, rhs); + } + } else { + static_assert(false, "Unsupported reduction operation"); + } + } +}; + +} // namespace + +namespace ov { +namespace mlir { + +template +ReducePattern::ReducePattern() + : MarkPattern(std::make_shared(OVOp::get_type_info_static()), ConvertReduce()) {} + +// Explicit template instantiations +template class ReducePattern; +template class ReducePattern; +template class ReducePattern; +template class ReducePattern; +template class ReducePattern; + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/reduce.hpp b/src/common/transformations/src/transformations/mlir/op/reduce.hpp new file mode 100644 index 00000000000000..2e1d592a02f32b --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/op/reduce.hpp @@ -0,0 +1,20 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "../conversion_context.hpp" + +namespace ov { +namespace mlir { + +template +class ReducePattern : public MarkPattern { +public: + OPENVINO_RTTI("ReducePattern", "0"); + ReducePattern(); +}; + +} // namespace mlir +} // namespace ov From cb252b1ccfac0270c69cc7c7493c68c989d2e2f6 Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Mon, 13 Apr 2026 11:33:32 +0200 Subject: [PATCH 060/121] Separate mlir-converters from ov-patterns (#95) Signed-off-by: dchigarev --- .../src/transformations/mlir/common/README.md | 77 +++++++++ .../mlir/common/conversion_context.cpp | 38 +++++ .../mlir/common/conversion_context.hpp | 50 ++++++ .../mlir/{ => common}/convert_common.cpp | 148 ++++++------------ .../mlir/{ => common}/convert_common.hpp | 18 +-- .../mlir/common/converters/binary_eltwise.hpp | 59 +++++++ .../converters/concat.hpp} | 24 +-- .../floor.cpp => common/converters/floor.hpp} | 27 +--- .../converters/gather.hpp} | 42 ++--- .../converters/matmul.hpp} | 43 ++--- .../converters/reduce.hpp} | 31 +--- .../relu.cpp => common/converters/relu.hpp} | 29 ++-- .../sdpa.cpp => common/converters/sdpa.hpp} | 34 ++-- .../converters/shape_of.hpp} | 26 +-- .../slice.cpp => common/converters/slice.hpp} | 28 ++-- .../converters/squeeze.hpp} | 24 +-- .../converters/transpose.hpp} | 26 +-- .../converters/unsqueeze.hpp} | 24 +-- .../mlir/{ => common}/typedefs.hpp | 0 .../mlir/conversion/patterns.cpp | 133 ++++++++++++++++ .../mlir/conversion/patterns.hpp | 106 +++++++++++++ .../src/transformations/mlir/convert.cpp | 30 ++-- ...ersion_context.cpp => graph_converter.cpp} | 37 ++--- ...ersion_context.hpp => graph_converter.hpp} | 22 +-- .../src/transformations/mlir/mlir_op.hpp | 2 +- .../mlir/op/binary_eltwise.cpp | 91 ----------- .../mlir/op/binary_eltwise.hpp | 42 ----- .../src/transformations/mlir/op/concat.hpp | 23 --- .../src/transformations/mlir/op/floor.hpp | 23 --- .../src/transformations/mlir/op/gather.hpp | 24 --- .../src/transformations/mlir/op/matmul.hpp | 24 --- .../src/transformations/mlir/op/reduce.hpp | 20 --- .../src/transformations/mlir/op/relu.hpp | 23 --- .../src/transformations/mlir/op/sdpa.hpp | 23 --- .../src/transformations/mlir/op/shape_of.hpp | 23 --- .../src/transformations/mlir/op/slice.hpp | 23 --- .../src/transformations/mlir/op/squeeze.hpp | 23 --- .../src/transformations/mlir/op/transpose.hpp | 23 --- .../src/transformations/mlir/op/unsqueeze.hpp | 24 --- .../transformations/mlir/subgraph_tracker.hpp | 2 +- 40 files changed, 662 insertions(+), 827 deletions(-) create mode 100644 src/common/transformations/src/transformations/mlir/common/README.md create mode 100644 src/common/transformations/src/transformations/mlir/common/conversion_context.cpp create mode 100644 src/common/transformations/src/transformations/mlir/common/conversion_context.hpp rename src/common/transformations/src/transformations/mlir/{ => common}/convert_common.cpp (84%) rename src/common/transformations/src/transformations/mlir/{ => common}/convert_common.hpp (100%) create mode 100644 src/common/transformations/src/transformations/mlir/common/converters/binary_eltwise.hpp rename src/common/transformations/src/transformations/mlir/{op/concat.cpp => common/converters/concat.hpp} (69%) rename src/common/transformations/src/transformations/mlir/{op/floor.cpp => common/converters/floor.hpp} (62%) rename src/common/transformations/src/transformations/mlir/{op/gather.cpp => common/converters/gather.hpp} (64%) rename src/common/transformations/src/transformations/mlir/{op/matmul.cpp => common/converters/matmul.hpp} (55%) rename src/common/transformations/src/transformations/mlir/{op/reduce.cpp => common/converters/reduce.hpp} (90%) rename src/common/transformations/src/transformations/mlir/{op/relu.cpp => common/converters/relu.hpp} (58%) rename src/common/transformations/src/transformations/mlir/{op/sdpa.cpp => common/converters/sdpa.hpp} (90%) rename src/common/transformations/src/transformations/mlir/{op/shape_of.cpp => common/converters/shape_of.hpp} (63%) rename src/common/transformations/src/transformations/mlir/{op/slice.cpp => common/converters/slice.hpp} (60%) rename src/common/transformations/src/transformations/mlir/{op/squeeze.cpp => common/converters/squeeze.hpp} (72%) rename src/common/transformations/src/transformations/mlir/{op/transpose.cpp => common/converters/transpose.hpp} (69%) rename src/common/transformations/src/transformations/mlir/{op/unsqueeze.cpp => common/converters/unsqueeze.hpp} (82%) rename src/common/transformations/src/transformations/mlir/{ => common}/typedefs.hpp (100%) create mode 100644 src/common/transformations/src/transformations/mlir/conversion/patterns.cpp create mode 100644 src/common/transformations/src/transformations/mlir/conversion/patterns.hpp rename src/common/transformations/src/transformations/mlir/{conversion_context.cpp => graph_converter.cpp} (67%) rename src/common/transformations/src/transformations/mlir/{conversion_context.hpp => graph_converter.hpp} (69%) delete mode 100644 src/common/transformations/src/transformations/mlir/op/binary_eltwise.cpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/binary_eltwise.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/concat.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/floor.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/gather.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/matmul.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/reduce.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/relu.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/sdpa.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/shape_of.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/slice.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/squeeze.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/transpose.hpp delete mode 100644 src/common/transformations/src/transformations/mlir/op/unsqueeze.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/README.md b/src/common/transformations/src/transformations/mlir/common/README.md new file mode 100644 index 00000000000000..e65de8cd6e0dcd --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/common/README.md @@ -0,0 +1,77 @@ +# OV-nodes to MLIR-linalg converters + +This folder contains converters and helper classes to convert certain OV operations to MLIR-linalg code. + +## Structure + +### `converters/` + +Contains `.hpp` files, each representing a converter for a specific OV operation or a class of operations (matmul, reduction, binary-elementwise). A converter must implement the following interface — it takes a conversion context and an OV node, produces a sequence of MLIR operations, and returns the final op: + +```c++ +mlir::Operation* operator()(ov::mlir::ConversionContext& context, std::shared_ptr node) +``` + +### `conversion_context` + +`ov::mlir::ConversionContext` is a class that provides converters with: +- `mlir::Context` +- `mlir::OpBuilder` +- Mapping between `ov::Node` inputs and MLIR tensors +- Mapping between dynamic-dimension symbols and their MLIR values + +```c++ +class ConversionContext { +public: + using getInputsFn = std::function(NodePtr)>; + using getDimValueFn = std::function; + + getInputsFn getInputs; + getDimValueFn getDimValue; + + ConversionContext( + mlir::MLIRContext* context, mlir::OpBuilder* block_builder, + getInputsFn getInputs, getDimValueFn getDimValue + ); + ... +``` + +A higher-level class responsible for whole-graph conversion is expected to encapsulate a `ConversionContext` instance and provide the necessary callbacks to resolve mapped inputs. + +Example: + +```c++ +class OvGraphImporter { + ov::mlir::ConversionContext _ctx; + ov::Model _model; + mlir::ModuleOp _module; + ... + using Converter = std::function; + +public: + OvGraphImporter(mlir::Context ctx, mlir::OpBuilder builder, ov::Model model): + _ctx( + ctx, builder, + [this](Node node){this->getInputs(node)}, + [this](Dimension dim) {this->getDimension(dim)} + ) + { + // build main-func-op in _module; + // build ov-inputs -> mlir-tensors mapping + } + + void import() { + for (auto op : model.get_ordered_ops()) { + // find a suitable converter from 'mlir/common/converters/*.hpp' + Converter converter = findMatchingConverter(op); + auto mlirOp = converter(_ctx, op); + addToOutputsMapping(mlirOp, op); + } + } + ... +} +``` + +### `convert_common` + +Contains general utility functions (creating MLIR locations/constants, shape/type importing) used by the converters. diff --git a/src/common/transformations/src/transformations/mlir/common/conversion_context.cpp b/src/common/transformations/src/transformations/mlir/common/conversion_context.cpp new file mode 100644 index 00000000000000..62a4c1f9d2e5ac --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/common/conversion_context.cpp @@ -0,0 +1,38 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +// #include "mlir/IR/BuiltinAttributes.h" +// #include "mlir/IR/BuiltinTypes.h" + +#include "conversion_context.hpp" + + +namespace ov { +namespace mlir { + +using namespace ::mlir; + +ConversionContext::ConversionContext( + mlir::MLIRContext* context, mlir::OpBuilder* block_builder, + getInputsFn getInputs, getDimValueFn getDimValue +) + : context(context), + block_builder(block_builder), + getInputs(getInputs), + getDimValue(getDimValue) {} + + +SmallVector ConversionContext::get_dynamic_dimension_values (const PartialShape& shape) { + SmallVector dims; + for (const auto& dim: shape) { + if (dim.is_dynamic()) { + dims.push_back(getDimValue(dim)); + } + } + return dims; +} + + +} // namespace mlir +} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/common/conversion_context.hpp b/src/common/transformations/src/transformations/mlir/common/conversion_context.hpp new file mode 100644 index 00000000000000..73e1655eac1933 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/common/conversion_context.hpp @@ -0,0 +1,50 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "mlir/IR/Value.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Builders.h" + +#include "typedefs.hpp" +#include "convert_common.hpp" + +namespace ov { +namespace mlir { + +using ::mlir::Value; +using ::mlir::MLIRContext; +using ::mlir::OpBuilder; +using ::mlir::Operation; +using ::mlir::SmallVector; +using ::mlir::ValueRange; + +class ConversionContext { +public: + using getInputsFn = std::function(NodePtr)>; + using getDimValueFn = std::function; + using NodeOutputMap = std::map, mlir::Value>; + + mlir::MLIRContext* context; + mlir::OpBuilder* block_builder; + getInputsFn getInputs; + getDimValueFn getDimValue; + + ConversionContext( + mlir::MLIRContext* context, mlir::OpBuilder* block_builder, + getInputsFn getInputs, getDimValueFn getDimValue + ); + + mlir::OpBuilder& builder() { + return *block_builder; + } + + SmallVector get_dynamic_dimension_values(const PartialShape& shape); +}; + +} // namespace mlir +} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/convert_common.cpp b/src/common/transformations/src/transformations/mlir/common/convert_common.cpp similarity index 84% rename from src/common/transformations/src/transformations/mlir/convert_common.cpp rename to src/common/transformations/src/transformations/mlir/common/convert_common.cpp index e35e3d5dd2366f..3d7581ce0bbcef 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.cpp +++ b/src/common/transformations/src/transformations/mlir/common/convert_common.cpp @@ -6,65 +6,9 @@ #include - -namespace { - -using namespace mlir; - -IntegerType getSInt4Type(MLIRContext* ctx) { - return IntegerType::get(ctx, 4, IntegerType::Signed); -} - -IntegerType getSInt8Type(MLIRContext* ctx) { - return IntegerType::get(ctx, 8, IntegerType::Signed); -} - -IntegerType getSInt16Type(MLIRContext* ctx) { - return IntegerType::get(ctx, 16, IntegerType::Signed); -} - -IntegerType getSInt32Type(MLIRContext* ctx) { - return IntegerType::get(ctx, 32, IntegerType::Signed); -} - -IntegerType getSInt64Type(MLIRContext* ctx) { - return IntegerType::get(ctx, 64, IntegerType::Signed); -} - -IntegerType getUInt4Type(MLIRContext* ctx) { - return IntegerType::get(ctx, 4, IntegerType::Unsigned); -} - -IntegerType getUInt8Type(MLIRContext* ctx) { - return IntegerType::get(ctx, 8, IntegerType::Unsigned); -} - -IntegerType getUInt16Type(MLIRContext* ctx) { - return IntegerType::get(ctx, 16, IntegerType::Unsigned); -} - -IntegerType getUInt32Type(MLIRContext* ctx) { - return IntegerType::get(ctx, 32, IntegerType::Unsigned); -} - -IntegerType getUInt64Type(MLIRContext* ctx) { - return IntegerType::get(ctx, 64, IntegerType::Unsigned); -} - -IntegerType getBool8Type(MLIRContext* ctx) { - // Signless 8-bit integer use for BOOL, to distinguish it from U8 - return IntegerType::get(ctx, 8, IntegerType::Signless); -} - -} - namespace ov { namespace mlir { -bool is_debug() { - return util::getenv_bool("OV_MLIR_DEBUG", false); -} - Location createLayerLocation(MLIRContext* ctx, const std::string& layerName, const std::string& layerType) { const auto layerNameAttr = StringAttr::get(ctx, layerName); const auto nameLoc = NameLoc::get(layerNameAttr); @@ -131,6 +75,52 @@ Location createLocation(MLIRContext* ctx, NodePtr node) { return createLayerLocation(ctx, node->get_friendly_name(), node->get_type_name()); } +BroadcastDimensions broadcast_dimensions(const PartialShape& src, const PartialShape& dst) { + assert(statically_broadcastable(src, dst)); + + auto src_rank = src.rank().get_length(); + auto dst_rank = dst.rank().get_length(); + auto offset = dst_rank - src_rank; + + BroadcastDimensions result; + auto& [collapse_groups, dimensions] = result; + ReassociationIndices group; + bool group_bonded = false; // true if `group` has a non-brodcasted dimension + + size_t dst_i = 0; // dimension index in the `dst` shape + for(; dst_i < offset; ++dst_i) { + dimensions.push_back(dst_i); + } + for(; dst_i < dst_rank; ++dst_i) { + auto src_i = dst_i - offset; + auto src_d = src[src_i]; + auto dst_d = dst[dst_i]; + if(has_broadcast(src_d, dst_d)) { + dimensions.push_back(dst_i); + } else { + if(group_bonded) { + collapse_groups.emplace_back(group); + group = ReassociationIndices(); + } else { + group_bonded = true; + } + } + group.push_back(src_i); + } + + if(group_bonded && !group.empty()) { + collapse_groups.emplace_back(group); + } + + assert(dst_rank - dimensions.size() == collapse_groups.size()); + + return result; +} + +bool symbol_ancestor_less (SymbolPtr x, SymbolPtr y) { + return ov::symbol::ancestor_of(x) < ov::symbol::ancestor_of(y); +} + bool elementwise_no_broadcast_predicate(const ov::Output& output) { if (has_dynamic_rank(output.get_node_shared_ptr())) { return false; @@ -209,50 +199,8 @@ bool statically_broadcastable(const PartialShape& from, const PartialShape& to) return true; } -BroadcastDimensions broadcast_dimensions(const PartialShape& src, const PartialShape& dst) { - assert(statically_broadcastable(src, dst)); - - auto src_rank = src.rank().get_length(); - auto dst_rank = dst.rank().get_length(); - auto offset = dst_rank - src_rank; - - BroadcastDimensions result; - auto& [collapse_groups, dimensions] = result; - ReassociationIndices group; - bool group_bonded = false; // true if `group` has a non-brodcasted dimension - - size_t dst_i = 0; // dimension index in the `dst` shape - for(; dst_i < offset; ++dst_i) { - dimensions.push_back(dst_i); - } - for(; dst_i < dst_rank; ++dst_i) { - auto src_i = dst_i - offset; - auto src_d = src[src_i]; - auto dst_d = dst[dst_i]; - if(has_broadcast(src_d, dst_d)) { - dimensions.push_back(dst_i); - } else { - if(group_bonded) { - collapse_groups.emplace_back(group); - group = ReassociationIndices(); - } else { - group_bonded = true; - } - } - group.push_back(src_i); - } - - if(group_bonded && !group.empty()) { - collapse_groups.emplace_back(group); - } - - assert(dst_rank - dimensions.size() == collapse_groups.size()); - - return result; -} - -bool symbol_ancestor_less (SymbolPtr x, SymbolPtr y) { - return ov::symbol::ancestor_of(x) < ov::symbol::ancestor_of(y); +bool is_debug() { + return util::getenv_bool("OV_MLIR_DEBUG", false); } } // namespace mlir diff --git a/src/common/transformations/src/transformations/mlir/convert_common.hpp b/src/common/transformations/src/transformations/mlir/common/convert_common.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/convert_common.hpp rename to src/common/transformations/src/transformations/mlir/common/convert_common.hpp index 43da4220ef0d0d..9d1fd473d640b9 100644 --- a/src/common/transformations/src/transformations/mlir/convert_common.hpp +++ b/src/common/transformations/src/transformations/mlir/common/convert_common.hpp @@ -19,13 +19,13 @@ namespace ov { namespace mlir { +using namespace ::mlir; + bool is_debug(); #define OPENVINO_MLIR_DEBUG(X) do if(::ov::mlir::is_debug()) { X; } while(false) #define OPENVINO_MLIR_DEBUG_PRINT(X) do if(::ov::mlir::is_debug()) { ::std::cerr << X; } while(false) -using namespace ::mlir; - Location createLayerLocation(MLIRContext* ctx, const std::string& layerName, const std::string& layerType); SmallVector importShape(const ov::PartialShape& shape); @@ -38,8 +38,6 @@ RankedTensorType importTensor(MLIRContext* ctx, Location createLocation(MLIRContext* ctx, NodePtr node); -bool elementwise_no_broadcast_predicate(const ov::Output& output); - template mlir::arith::ConstantOp getConstant(OpBuilder& builder, const mlir::Type& type, @@ -63,6 +61,13 @@ mlir::arith::ConstantOp getConstant(OpBuilder& builder, return getConstant(builder, importPrecision(builder.getContext(), precision), value, loc); } +using BroadcastDimensions = std::tuple, SmallVector>; +BroadcastDimensions broadcast_dimensions(const PartialShape& from, const PartialShape& to); + +bool elementwise_no_broadcast_predicate(const ov::Output& output); + +bool symbol_ancestor_less (SymbolPtr x, SymbolPtr y); + bool has_dynamic_rank(NodePtr node); bool are_equal_dimensions(Dimension d1, Dimension d2); @@ -71,10 +76,5 @@ bool has_broadcast(Dimension from, Dimension to); bool statically_broadcastable(const PartialShape& from, const PartialShape& to); -using BroadcastDimensions = std::tuple, SmallVector>; -BroadcastDimensions broadcast_dimensions(const PartialShape& from, const PartialShape& to); - -bool symbol_ancestor_less (SymbolPtr x, SymbolPtr y); - } // namespace mlir } // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/common/converters/binary_eltwise.hpp b/src/common/transformations/src/transformations/mlir/common/converters/binary_eltwise.hpp new file mode 100644 index 00000000000000..54f9da32f1aad1 --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/common/converters/binary_eltwise.hpp @@ -0,0 +1,59 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Value.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/Dialect/Linalg/Passes.h" + +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +namespace ov { +namespace mlir { + +using namespace ov; +using namespace ov::mlir; +using ::mlir::ValueRange; + +template +struct ConvertBinaryEltwise { + Operation* operator()(ConversionContext& context, NodePtr node) { + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto inputs = context.getInputs(node); + const auto ov_output_element_type = node->get_output_element_type(0); + const auto ov_output_shape = node->get_output_partial_shape(0); + auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); + const int output_rank = ov_output_shape.rank().get_length(); + + SmallVector dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); + + SmallVector broadcasted_inputs; + for(size_t i = 0; i < inputs.size(); ++i) { + auto [collapse_groups, dimensions] = broadcast_dimensions(node->get_input_partial_shape(i), ov_output_shape); + if(!dimensions.empty()) { + // FIXME: Find a way to avoid dimension squeezing before applying linalg.broadcast + // Step 1: Squeeze input shape to eliminate broadcasted dimensions + auto squeezed = tensor::CollapseShapeOp::create(builder, loc, inputs[i], collapse_groups); + // Step 2: Broadcast squeezed shape to the target shape + auto empty = tensor::EmptyOp::create(builder, loc, outType, dynamic_dimensions); + auto op = linalg::BroadcastOp::create(builder, loc, squeezed, empty, dimensions); + broadcasted_inputs.push_back(op.getResult()[0]); + } else { + broadcasted_inputs.push_back(inputs[i]); + } + } + + auto empty = tensor::EmptyOp::create(builder, loc, outType, dynamic_dimensions); + auto op = MlirBinOpBuilder::create(builder, loc, ValueRange(broadcasted_inputs), ValueRange{empty}); + return op; + } +}; + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/concat.cpp b/src/common/transformations/src/transformations/mlir/common/converters/concat.hpp similarity index 69% rename from src/common/transformations/src/transformations/mlir/op/concat.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/concat.hpp index 951ecbb23b8385..ba32e3e1274872 100644 --- a/src/common/transformations/src/transformations/mlir/op/concat.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/concat.hpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // +#pragma once + #include "mlir/Dialect/Linalg/Passes.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" @@ -9,15 +11,13 @@ #include "openvino/opsets/opset1.hpp" #include "openvino/pass/pattern/op/wrap_type.hpp" -#include "concat.hpp" #include "../convert_common.hpp" -namespace { - -using namespace ov::mlir; +namespace ov { +namespace mlir { struct ConvertConcat { - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); const auto inputs = context.getInputs(node); @@ -32,21 +32,11 @@ struct ConvertConcat { axis += rank; } - auto concat = builder.create(loc, axis, mlir::ValueRange{inputs}); - context.addOutputs(node, concat); + auto concat = tensor::ConcatOp::create(builder, loc, axis, mlir::ValueRange{inputs}); + return concat; } }; -} // namespace - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; -using namespace ov::op; - -ConcatPattern::ConcatPattern() : MarkPattern(wrap_type(), ConvertConcat()) {} - } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/floor.cpp b/src/common/transformations/src/transformations/mlir/common/converters/floor.hpp similarity index 62% rename from src/common/transformations/src/transformations/mlir/op/floor.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/floor.hpp index 32758fe8f564b6..6ed7a7a88e3f6a 100644 --- a/src/common/transformations/src/transformations/mlir/op/floor.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/floor.hpp @@ -2,22 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 // +#pragma once + #include "mlir/Dialect/Linalg/Passes.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" #include #include "openvino/pass/pattern/op/wrap_type.hpp" -#include "floor.hpp" #include "../convert_common.hpp" -namespace { - -using namespace ov::mlir; +namespace ov { +namespace mlir { struct ConvertFloor { - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); const auto input = context.getInputs(node)[0]; @@ -25,22 +25,11 @@ struct ConvertFloor { const auto ov_output_shape = node->get_output_partial_shape(0); auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); auto dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); - auto empty = builder.create(loc, outType, dynamic_dimensions); - auto floor = builder.create(loc, mlir::ValueRange{input}, mlir::ValueRange{empty}); - context.addOutputs(node, floor); + auto empty = tensor::EmptyOp::create(builder, loc, outType, dynamic_dimensions); + auto floor = linalg::FloorOp::create(builder, loc, mlir::ValueRange{input}, mlir::ValueRange{empty}); + return floor; } }; -} // namespace - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; -using namespace ov::op; - -FloorPattern::FloorPattern() - : MarkPattern(wrap_type({any_input()}), ConvertFloor()) {} - } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/gather.cpp b/src/common/transformations/src/transformations/mlir/common/converters/gather.hpp similarity index 64% rename from src/common/transformations/src/transformations/mlir/op/gather.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/gather.hpp index 229f24380e5ef2..d4491f4eea5a40 100644 --- a/src/common/transformations/src/transformations/mlir/op/gather.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/gather.hpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // +#pragma once + #include "mlir/Dialect/Shape/IR/Shape.h" #include "mlir/Dialect/Linalg/Passes.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" @@ -10,15 +12,13 @@ #include #include "openvino/pass/pattern/op/wrap_type.hpp" -#include "gather.hpp" #include "../convert_common.hpp" -namespace { - -using namespace ov::mlir; +namespace ov { +namespace mlir { struct ConvertGather { - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { // TODO: support batch attribute auto loc = createLocation(context.context, node); auto& builder = context.builder(); @@ -44,44 +44,34 @@ struct ConvertGather { SmallVector new_shape({1}); indices_type = RankedTensorType::get(new_shape, importPrecision(context.context, ov_index_element_type)); SmallVector reassociation; // intentionally empty for scalar - auto expanded = builder.create(loc, indices_type, indices, reassociation); + auto expanded = tensor::ExpandShapeOp::create(builder, loc, indices_type, indices, reassociation); indices_expanded = expanded.getResult(); } // Convert negative indices into positive ones: compare to zero and select from orinal or a sum based on // the resulting predicate. - auto empty = builder.create(loc, indices_type, dynamic_index_dims); + auto empty = tensor::EmptyOp::create(builder, loc, indices_type, dynamic_index_dims); auto zero = getConstant(builder, ov_index_element_type, 0); - auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); + auto fill = linalg::FillOp::create(builder, loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); auto pred = arith::CmpIPredicate::slt; - auto cmpi = builder.create(loc, pred, indices_expanded, fill.getResult(0)); - auto shape_of = builder.create(loc, mlir::ValueRange{input}); - auto cast = builder.create(loc, indices_type, mlir::ValueRange{shape_of}); + auto cmpi = arith::CmpIOp::create(builder, loc, pred, indices_expanded, fill.getResult(0)); + auto shape_of = shape::ShapeOfOp::create(builder, loc, mlir::ValueRange{input}); + auto cast = arith::IndexCastOp::create(builder, loc, indices_type, mlir::ValueRange{shape_of}); - auto empty_add = builder.create(loc, indices_expanded.getType(), dynamic_index_dims); - auto add = builder.create(loc, mlir::ValueRange{cast.getResult(), indices_expanded}, mlir::ValueRange{empty_add}); - auto select = builder.create(loc, mlir::ValueRange{cmpi.getResult(), add.getResult(0), indices_expanded}, mlir::ValueRange{empty_add}); + auto empty_add = tensor::EmptyOp::create(builder, loc, indices_expanded.getType(), dynamic_index_dims); + auto add = linalg::AddOp::create(builder, loc, mlir::ValueRange{cast.getResult(), indices_expanded}, mlir::ValueRange{empty_add}); + auto select = linalg::SelectOp::create(builder, loc, mlir::ValueRange{cmpi.getResult(), add.getResult(0), indices_expanded}, mlir::ValueRange{empty_add}); auto gather_node = std::dynamic_pointer_cast(node); assert(gather_node && "Expected a gather node"); int64_t axis = gather_node->get_axis(); llvm::SmallVector gather_dims{axis}; - auto gather = builder.create(loc, out_type, input, select.getResult(0), gather_dims, false); - context.addOutputs(node, gather); + auto gather = tensor::GatherOp::create(builder, loc, out_type, input, select.getResult(0), gather_dims, false); + return gather; } }; -} // namespace - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; -using namespace ov::op; - -GatherPattern::GatherPattern() : MarkPattern(wrap_type({any_input(), any_input(), any_input()}), ConvertGather()) {} - } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/matmul.cpp b/src/common/transformations/src/transformations/mlir/common/converters/matmul.hpp similarity index 55% rename from src/common/transformations/src/transformations/mlir/op/matmul.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/matmul.hpp index b8c50db1ed4f31..54410e6f577ce4 100644 --- a/src/common/transformations/src/transformations/mlir/op/matmul.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/matmul.hpp @@ -2,22 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 // +#pragma once + #include "mlir/Dialect/Tensor/IR/Tensor.h" #include "mlir/Dialect/Linalg/Passes.h" #include #include "openvino/pass/pattern/op/wrap_type.hpp" -#include "matmul.hpp" #include "../convert_common.hpp" -namespace { - -using namespace ov::mlir; +namespace ov { +namespace mlir { struct ConvertMatMul { - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); // TODO: Support broadcasts @@ -26,9 +26,9 @@ struct ConvertMatMul { const auto ov_output_shape = node->get_output_partial_shape(0); auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); auto dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); - auto empty = builder.create(loc, outType, dynamic_dimensions); + auto empty = tensor::EmptyOp::create(builder, loc, outType, dynamic_dimensions); auto zero = getConstant(builder, ov_output_element_type, 0); - auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); + auto fill = linalg::FillOp::create(builder, loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); mlir::SmallVector ins{inputs[0], inputs[1]}; mlir::SmallVector outs{fill.getResult(0)}; @@ -41,37 +41,16 @@ struct ConvertMatMul { Operation* matmul; if (isTransposedA) { - matmul = builder.create(loc, ins, outs); + matmul = linalg::MatmulTransposeAOp::create(builder, loc, ins, outs); } else if (isTransposedB) { - matmul = builder.create(loc, ins, outs); + matmul = linalg::MatmulTransposeBOp::create(builder, loc, ins, outs); } else { - matmul = builder.create(loc, ins, outs); + matmul = linalg::MatmulOp::create(builder, loc, ins, outs); } - context.addOutputs(node, matmul); + return matmul; } }; -} - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; -using namespace ov::op; - -MatMulPattern::MatMulPattern() : MarkPattern( - wrap_type({any_input(), any_input()}, [](const Output& output) { - auto node = std::dynamic_pointer_cast(output.get_node_shared_ptr()); - assert(node); - // FIXME: current code limitation - return !has_dynamic_rank(node) && !(node->get_transpose_a() && node->get_transpose_b()) && - node->get_input_partial_shape(0).rank().get_length() == 2 && - node->get_input_partial_shape(1).rank().get_length() == 2; - }), - ConvertMatMul()) { - } - - } // namespace mlir } // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/op/reduce.cpp b/src/common/transformations/src/transformations/mlir/common/converters/reduce.hpp similarity index 90% rename from src/common/transformations/src/transformations/mlir/op/reduce.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/reduce.hpp index 3458e2daff06c7..49c7db91cdd41f 100644 --- a/src/common/transformations/src/transformations/mlir/op/reduce.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/reduce.hpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "reduce.hpp" +#pragma once #include #include @@ -22,10 +22,9 @@ #include "mlir/IR/Value.h" #include "openvino/pass/pattern/op/wrap_type.hpp" -namespace { +namespace ov { +namespace mlir { -using namespace ov; -using namespace ov::mlir; using Value = ::mlir::Value; using ValueRange = ::mlir::ValueRange; @@ -33,7 +32,7 @@ template struct ConvertReduce { ConvertReduce() = default; - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { auto el_type = importPrecision(context.context, node->get_input_element_type(0)); auto input_shape = node->get_input_partial_shape(0); auto input_rank = input_shape.rank().get_length(); @@ -107,7 +106,7 @@ struct ConvertReduce { result = ::mlir::linalg::BroadcastOp::create(builder, loc, result, empty, reduction_axes).getResult()[0]; } - context.nodeOutputMap[node->output(0)] = result; + return result.getDefiningOp(); } private: @@ -133,7 +132,7 @@ struct ConvertReduce { } else if constexpr (std::is_same_v) { return getConstant(builder, type, 1, loc); } else { - static_assert(false, "Unsupported reduction operation"); + static_assert(sizeof(OVOp) == 0, "Unsupported reduction operation"); } } @@ -168,26 +167,10 @@ struct ConvertReduce { return ::mlir::arith::MulIOp::create(builder, loc, lhs, rhs); } } else { - static_assert(false, "Unsupported reduction operation"); + static_assert(sizeof(OVOp) == 0, "Unsupported reduction operation"); } } }; -} // namespace - -namespace ov { -namespace mlir { - -template -ReducePattern::ReducePattern() - : MarkPattern(std::make_shared(OVOp::get_type_info_static()), ConvertReduce()) {} - -// Explicit template instantiations -template class ReducePattern; -template class ReducePattern; -template class ReducePattern; -template class ReducePattern; -template class ReducePattern; - } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/relu.cpp b/src/common/transformations/src/transformations/mlir/common/converters/relu.hpp similarity index 58% rename from src/common/transformations/src/transformations/mlir/op/relu.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/relu.hpp index 6f7157f9bd4bd4..1e9d4c9b8c8463 100644 --- a/src/common/transformations/src/transformations/mlir/op/relu.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/relu.hpp @@ -2,21 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 // +#pragma once + #include "mlir/Dialect/Tensor/IR/Tensor.h" #include "mlir/Dialect/Linalg/Passes.h" #include #include "openvino/pass/pattern/op/wrap_type.hpp" -#include "relu.hpp" #include "../convert_common.hpp" -namespace { - -using namespace ov::mlir; +namespace ov { +namespace mlir { struct ConvertRelu { - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); const auto input = context.getInputs(node)[0]; @@ -24,25 +24,14 @@ struct ConvertRelu { const auto ov_output_shape = node->get_output_partial_shape(0); auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); auto dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); - auto empty = builder.create(loc, outType, dynamic_dimensions); + auto empty = tensor::EmptyOp::create(builder, loc, outType, dynamic_dimensions); auto zero = getConstant(builder, ov_output_element_type, 0); - auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); + auto fill = linalg::FillOp::create(builder, loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); auto relu = - builder.create(loc, mlir::ValueRange{input, fill.getResult(0)}, mlir::ValueRange{empty}); - context.addOutputs(node, relu); + linalg::MaxOp::create(builder, loc, mlir::ValueRange{input, fill.getResult(0)}, mlir::ValueRange{empty}); + return relu; } }; -} // namespace - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; -using namespace ov::op; - -ReluPattern::ReluPattern() - : MarkPattern(wrap_type({any_input()}, elementwise_no_broadcast_predicate), ConvertRelu()) {} - } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/sdpa.cpp b/src/common/transformations/src/transformations/mlir/common/converters/sdpa.hpp similarity index 90% rename from src/common/transformations/src/transformations/mlir/op/sdpa.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/sdpa.hpp index eaa4a30394ab71..ede3d72d8d9934 100644 --- a/src/common/transformations/src/transformations/mlir/op/sdpa.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/sdpa.hpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // +#pragma once + #include "mlir/Dialect/Tensor/IR/Tensor.h" #include "mlir/Dialect/Linalg/Passes.h" @@ -17,12 +19,10 @@ #include "gc/Dialect/Linalgx/LinalgxOps.h" #include "mlir/IR/AffineExpr.h" -#include "sdpa.hpp" #include "../convert_common.hpp" -namespace { - -using namespace ov::mlir; +namespace ov { +namespace mlir { struct ConvertSDPA { static SmallVector getStandardAttentionIndexingMaps(MLIRContext *ctx, @@ -63,7 +63,7 @@ struct ConvertSDPA { return {}; } - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); const auto inputs = context.getInputs(node); @@ -113,11 +113,11 @@ struct ConvertSDPA { if (maskRank == 3) { // [1, M, N] → [M, N]: collapse dims [0,1] and [2] SmallVector reassoc = {{0, 1}, {2}}; - mask = builder.create(loc, mask, reassoc); + mask = tensor::CollapseShapeOp::create(builder, loc, mask, reassoc); } else if (maskRank == 4) { // [1, 1, M, N] → [M, N]: collapse dims [0,1,2] and [3] SmallVector reassoc = {{0, 1, 2}, {3}}; - mask = builder.create(loc, mask, reassoc); + mask = tensor::CollapseShapeOp::create(builder, loc, mask, reassoc); } // maskRank == 2: already 2D, no change needed } @@ -145,30 +145,20 @@ struct ConvertSDPA { const auto ov_output_shape = node->get_output_partial_shape(0); auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); auto dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); - auto empty = builder.create(loc, outType, dynamic_dimensions); + auto empty = tensor::EmptyOp::create(builder, loc, outType, dynamic_dimensions); auto zero = getConstant(builder, ov_output_element_type, 0); - auto fill = builder.create(loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); + auto fill = linalg::FillOp::create(builder, loc, mlir::ValueRange{zero}, mlir::ValueRange{empty}); SmallVector indexingMaps = getStandardAttentionIndexingMaps(context.context, hasMask, qRank); - Operation* sdpa = builder.create( + Operation* sdpa = linalgx::AttentionOp::create(builder, loc, fill.getResult(0).getType(), inputs[0], inputs[1], inputs[2], scale, fill.getResult(0), builder.getAffineMapArrayAttr(indexingMaps), mask); - context.addOutputs(node, sdpa); + return sdpa; } }; -} // namespace - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; -using namespace ov::op; - -SDPAPattern::SDPAPattern() - : MarkPattern(wrap_type(), ConvertSDPA()) {} - } // namespace mlir } // namespace ov + diff --git a/src/common/transformations/src/transformations/mlir/op/shape_of.cpp b/src/common/transformations/src/transformations/mlir/common/converters/shape_of.hpp similarity index 63% rename from src/common/transformations/src/transformations/mlir/op/shape_of.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/shape_of.hpp index 9b27e3f70a4a41..cafbba38edb57d 100644 --- a/src/common/transformations/src/transformations/mlir/op/shape_of.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/shape_of.hpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // +#pragma once + #include "mlir/Dialect/Shape/IR/Shape.h" #include "mlir/Dialect/Linalg/Passes.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" @@ -10,37 +12,25 @@ #include #include "openvino/pass/pattern/op/wrap_type.hpp" -#include "shape_of.hpp" #include "../convert_common.hpp" -namespace { - -using namespace ov::mlir; +namespace ov { +namespace mlir { struct ConvertShapeOf { - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); const auto ov_output_element_type = node->get_output_element_type(0); const auto ov_output_shape = node->get_output_partial_shape(0); const auto input = context.getInputs(node)[0]; - auto shapeOf = builder.create(loc, mlir::ValueRange{input}); + auto shapeOf = shape::ShapeOfOp::create(builder, loc, mlir::ValueRange{input}); auto casted_type = RankedTensorType::get(ArrayRef(importShape(ov_output_shape)), importPrecision(context.context, ov_output_element_type)); - auto cast = builder.create(loc, casted_type, mlir::ValueRange{shapeOf}); - context.addOutputs(node, cast); + auto cast = arith::IndexCastOp::create(builder, loc, casted_type, mlir::ValueRange{shapeOf}); + return cast; } }; -} // namespace - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; -using namespace ov::op; - -ShapeOfPattern::ShapeOfPattern() : MarkPattern(wrap_type({any_input()}), ConvertShapeOf()) {} - } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/slice.cpp b/src/common/transformations/src/transformations/mlir/common/converters/slice.hpp similarity index 60% rename from src/common/transformations/src/transformations/mlir/op/slice.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/slice.hpp index 49b3cc64a12e93..ece0f08e0e4f40 100644 --- a/src/common/transformations/src/transformations/mlir/op/slice.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/slice.hpp @@ -2,21 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 // +#pragma once + #include "mlir/Dialect/Linalg/Passes.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" #include #include "openvino/pass/pattern/op/wrap_type.hpp" -#include "slice.hpp" #include "../convert_common.hpp" -namespace { - -using namespace ov::mlir; +namespace ov { +namespace mlir { struct ConvertSlice { - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); const auto input = context.getInputs(node)[0]; @@ -30,24 +30,14 @@ struct ConvertSlice { auto dynamic_index_dims = context.get_dynamic_dimension_values(ov_index_shape); auto index_type = importTensor(context.context, ov_index_shape, ov_index_element_type); - auto empty = builder.create(loc, index_type, dynamic_index_dims); + auto empty = tensor::EmptyOp::create(builder, loc, index_type, dynamic_index_dims); // TODO: this only works for the all-positive numbers case. - auto sizes = builder.create(loc, mlir::ValueRange{stop, start}, mlir::ValueRange{empty}); - auto slice = builder.create(loc, input, mlir::ValueRange{start}, mlir::ValueRange{sizes.getResults()}, mlir::ValueRange{step}); - context.addOutputs(node, slice); + auto sizes = linalg::SubOp::create(builder, loc, mlir::ValueRange{stop, start}, mlir::ValueRange{empty}); + auto slice = tensor::ExtractSliceOp::create(builder, loc, input, mlir::ValueRange{start}, mlir::ValueRange{sizes.getResults()}, mlir::ValueRange{step}); + return slice; } }; -} // namespace - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; -using namespace ov::op; - -SlicePattern::SlicePattern() : MarkPattern(wrap_type({any_input(), any_input(), any_input(), any_input(), any_input()}), ConvertSlice()) {} - } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/squeeze.cpp b/src/common/transformations/src/transformations/mlir/common/converters/squeeze.hpp similarity index 72% rename from src/common/transformations/src/transformations/mlir/op/squeeze.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/squeeze.hpp index 025fb9b51d0acc..b2880da804e7ee 100644 --- a/src/common/transformations/src/transformations/mlir/op/squeeze.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/squeeze.hpp @@ -2,22 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 // +#pragma once + #include "mlir/Dialect/Linalg/Passes.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" #include #include "openvino/pass/pattern/op/wrap_type.hpp" -#include "squeeze.hpp" #include "../convert_common.hpp" -namespace { - -using namespace ov::mlir; +namespace ov { +namespace mlir { struct ConvertSqueeze { - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); const auto input = context.getInputs(node)[0]; @@ -37,20 +37,10 @@ struct ConvertSqueeze { } } - auto reshape = builder.create(loc, input, collapse_groups); - context.addOutputs(node, reshape); + auto reshape = tensor::CollapseShapeOp::create(builder, loc, input, collapse_groups); + return reshape; } }; -} // namespace - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; -using namespace ov::op; - -SqueezePattern::SqueezePattern() : MarkPattern(wrap_type({any_input()}), ConvertSqueeze()) {} - } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/transpose.cpp b/src/common/transformations/src/transformations/mlir/common/converters/transpose.hpp similarity index 69% rename from src/common/transformations/src/transformations/mlir/op/transpose.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/transpose.hpp index aa08258e131b4b..c5efab33df90b0 100644 --- a/src/common/transformations/src/transformations/mlir/op/transpose.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/transpose.hpp @@ -2,21 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 // +#pragma once + #include "mlir/Dialect/Linalg/Passes.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" #include #include "openvino/pass/pattern/op/wrap_type.hpp" -#include "transpose.hpp" #include "../convert_common.hpp" -namespace { - -using namespace ov::mlir; +namespace ov { +namespace mlir { struct ConvertTranspose { - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); const auto input = context.getInputs(node)[0]; @@ -33,22 +33,12 @@ struct ConvertTranspose { ov::Coordinate coords = const_order->get_coordinate_val(); SmallVector order(coords.begin(), coords.end()); - auto empty = builder.create(loc, out_type, dynamic_dimensions); - auto transpose = builder.create(loc, input, empty, order); - context.addOutputs(node, transpose); + auto empty = tensor::EmptyOp::create(builder, loc, out_type, dynamic_dimensions); + auto transpose = linalg::TransposeOp::create(builder, loc, input, empty, order); + return transpose; } }; -} // namespace - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; -using namespace ov::op; - -TransposePattern::TransposePattern() : MarkPattern(wrap_type({any_input(), any_input()}), ConvertTranspose()) {} - } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/unsqueeze.cpp b/src/common/transformations/src/transformations/mlir/common/converters/unsqueeze.hpp similarity index 82% rename from src/common/transformations/src/transformations/mlir/op/unsqueeze.cpp rename to src/common/transformations/src/transformations/mlir/common/converters/unsqueeze.hpp index 2e82195d2d912f..5024a3c61bc5f8 100644 --- a/src/common/transformations/src/transformations/mlir/op/unsqueeze.cpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/unsqueeze.hpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // +#pragma once + #include "mlir/Dialect/Shape/IR/Shape.h" #include "mlir/Dialect/Linalg/Passes.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" @@ -10,16 +12,14 @@ #include #include "openvino/pass/pattern/op/wrap_type.hpp" -#include "unsqueeze.hpp" #include "../convert_common.hpp" -namespace { - -using namespace ov::mlir; +namespace ov { +namespace mlir { struct ConvertUnsqueeze { - void operator()(ConversionContext& context, NodePtr node) { + Operation* operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); auto& builder = context.builder(); const auto input = context.getInputs(node)[0]; @@ -55,21 +55,11 @@ struct ConvertUnsqueeze { } auto result_type = RankedTensorType::get(shape, importPrecision(context.context, ov_output_element_type)); - auto expand_shape = builder.create(loc, result_type, input, expand_groups); - context.addOutputs(node, expand_shape); + auto expand_shape = tensor::ExpandShapeOp::create(builder, loc, result_type, input, expand_groups); + return expand_shape; } }; -} // namespace - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; -using namespace ov::op; - -UnsqueezePattern::UnsqueezePattern() : MarkPattern(wrap_type({any_input(), any_input()}), ConvertUnsqueeze()) {} - } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/typedefs.hpp b/src/common/transformations/src/transformations/mlir/common/typedefs.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/typedefs.hpp rename to src/common/transformations/src/transformations/mlir/common/typedefs.hpp diff --git a/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp b/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp new file mode 100644 index 00000000000000..89f9bf2c5a916f --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp @@ -0,0 +1,133 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "patterns.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "openvino/pass/pattern/op/wrap_type.hpp" + +#include "../common/converters/relu.hpp" +#include "../common/converters/concat.hpp" +#include "../common/converters/floor.hpp" +#include "../common/converters/gather.hpp" +#include "../common/converters/matmul.hpp" +#include "../common/converters/reduce.hpp" +#include "../common/converters/sdpa.hpp" +#include "../common/converters/shape_of.hpp" +#include "../common/converters/slice.hpp" +#include "../common/converters/squeeze.hpp" +#include "../common/converters/transpose.hpp" +#include "../common/converters/unsqueeze.hpp" +#include "../common/converters/binary_eltwise.hpp" + +namespace ov { +namespace mlir { + +using namespace ov::pass::pattern; +using namespace ov::op; + +ReluPattern::ReluPattern() + : MarkPattern(wrap_type({any_input()}, elementwise_no_broadcast_predicate), ConvertRelu()) {} + +ConcatPattern::ConcatPattern() + : MarkPattern(wrap_type(), ConvertConcat()) {} + +FloorPattern::FloorPattern() + : MarkPattern(wrap_type({any_input()}), ConvertFloor()) {} + +GatherPattern::GatherPattern() + : MarkPattern(wrap_type({any_input(), any_input(), any_input()}), ConvertGather()) {} + +MatMulPattern::MatMulPattern() + : MarkPattern( + wrap_type({any_input(), any_input()}, [](const Output& output) { + auto node = std::dynamic_pointer_cast(output.get_node_shared_ptr()); + assert(node); + // FIXME: current code limitation + return !has_dynamic_rank(node) && !(node->get_transpose_a() && node->get_transpose_b()) && + node->get_input_partial_shape(0).rank().get_length() == 2 && + node->get_input_partial_shape(1).rank().get_length() == 2; + }), + ConvertMatMul()) {} + +template +ReducePattern::ReducePattern() + : MarkPattern(std::make_shared(OVOp::get_type_info_static()), ConvertReduce()) {} + +// Explicit template instantiations +template class ReducePattern; +template class ReducePattern; +template class ReducePattern; +template class ReducePattern; +template class ReducePattern; + +SDPAPattern::SDPAPattern() + : MarkPattern(wrap_type(), ConvertSDPA()) {} + +ShapeOfPattern::ShapeOfPattern() + : MarkPattern(wrap_type({any_input()}), ConvertShapeOf()) {} + +SlicePattern::SlicePattern() + : MarkPattern(wrap_type({any_input(), any_input(), any_input(), any_input(), any_input()}), ConvertSlice()) {} + +SqueezePattern::SqueezePattern() + : MarkPattern(wrap_type({any_input()}), ConvertSqueeze()) {} + +TransposePattern::TransposePattern() + : MarkPattern(wrap_type({any_input(), any_input()}), ConvertTranspose()) {} + +UnsqueezePattern::UnsqueezePattern() + : MarkPattern(wrap_type({any_input(), any_input()}), ConvertUnsqueeze()) {} + +BinaryEltwisePatternBase::BinaryEltwisePatternBase( + NodeTypeInfo wrapped_type, GraphConverter::Convertor convertor, const std::set& element_types) + : MarkPattern( + std::make_shared( + wrapped_type, + [element_types](const Output& output) { + if (!element_types.empty() && !element_types.count(output.get_element_type())) { + return false; + } + auto node = output.get_node_shared_ptr(); + for (const auto& input : node->inputs()) { + if (!statically_broadcastable(input.get_partial_shape(), output.get_partial_shape())) { + return false; + } + } + return true; + }, + OutputVector{any_input(), any_input()}), + convertor) {} + +template +BinaryEltwisePattern::BinaryEltwisePattern(const std::set& element_types) + : BinaryEltwisePatternBase(OVOp::get_type_info_static(), ConvertBinaryEltwise(), element_types) {} + +// Explicit template instantiations +template class BinaryEltwisePattern; +template class BinaryEltwisePattern; +template class BinaryEltwisePattern; +template class BinaryEltwisePattern; + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp b/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp new file mode 100644 index 00000000000000..14bc1d0ed0978b --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp @@ -0,0 +1,106 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "../graph_converter.hpp" +// #include +// #include +// #include +// #include + +namespace ov { +namespace mlir { + +class ReluPattern : public MarkPattern { +public: + OPENVINO_RTTI("ReluPattern", "0"); + ReluPattern(); +}; + +class ConcatPattern : public MarkPattern { +public: + OPENVINO_RTTI("ConcatPattern", "0"); + ConcatPattern(); +}; + +class FloorPattern : public MarkPattern { +public: + OPENVINO_RTTI("FloorPattern", "0"); + FloorPattern(); +}; + +class GatherPattern : public MarkPattern { +public: + OPENVINO_RTTI("GatherPattern", "0"); + GatherPattern(); +}; + +class MatMulPattern : public MarkPattern { +public: + OPENVINO_RTTI("MatMulPattern", "0"); + MatMulPattern(); +}; + +template +class ReducePattern : public MarkPattern { +public: + OPENVINO_RTTI("ReducePattern", "0"); + ReducePattern(); +}; + +class SDPAPattern : public MarkPattern { +public: + OPENVINO_RTTI("SDPAPattern", "0"); + SDPAPattern(); +}; + +class ShapeOfPattern : public MarkPattern { +public: + OPENVINO_RTTI("ShapeOfPattern", "0"); + ShapeOfPattern(); +}; + +class SlicePattern : public MarkPattern { +public: + OPENVINO_RTTI("SlicePattern", "0"); + SlicePattern(); +}; + +class SqueezePattern : public MarkPattern { +public: + OPENVINO_RTTI("SqueezePattern", "0"); + SqueezePattern(); +}; + +class TransposePattern : public MarkPattern { +public: + OPENVINO_RTTI("TransposePattern", "0"); + TransposePattern(); +}; + +class UnsqueezePattern : public MarkPattern { +public: + OPENVINO_RTTI("UnsqueezePattern", "0"); + UnsqueezePattern(); +}; + +class BinaryEltwisePatternBase : public MarkPattern { +public: + OPENVINO_RTTI("BinaryEltwisePatternBase", "0"); + BinaryEltwisePatternBase(NodeTypeInfo wrapped_type, GraphConverter::Convertor convertor, + const std::set& element_types = {}); +}; + +template +class BinaryEltwisePattern : public BinaryEltwisePatternBase { +public: + BinaryEltwisePattern(const std::set& element_types = {}); + + BinaryEltwisePattern(const element::Type& element_type) + : BinaryEltwisePattern(std::set{element_type}) {} +}; + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index a3a5745c305ed5..7ac30b9e3e0ce9 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -23,8 +23,8 @@ #include // TODO: Prune unused headers -- it's hard to understand needed ones -#include "conversion_context.hpp" -#include "convert_common.hpp" +#include "graph_converter.hpp" +#include "common/convert_common.hpp" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Casting.h" #include "llvm/Support/InitLLVM.h" @@ -78,19 +78,7 @@ #endif #include "mlir_op.hpp" -#include "op/concat.hpp" -#include "op/matmul.hpp" -#include "op/relu.hpp" -#include "op/floor.hpp" -#include "op/gather.hpp" -#include "op/shape_of.hpp" -#include "op/slice.hpp" -#include "op/squeeze.hpp" -#include "op/transpose.hpp" -#include "op/unsqueeze.hpp" -#include "op/sdpa.hpp" -#include "op/binary_eltwise.hpp" -#include "op/reduce.hpp" +#include "conversion/patterns.hpp" #include "openvino/core/dimension.hpp" #include "openvino/core/rt_info.hpp" #include "openvino/core/symbol.hpp" @@ -160,7 +148,7 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, auto sysSpec = TargetSystemSpecAttr::get(context, {DataLayoutEntryAttr::get(deviceStr, deviceSpec)}); module.getOperation()->setAttr("#dlti.sys_spec", sysSpec); - ConversionContext conversion_context(context, &block_builder); + GraphConverter graph_converter(context, &block_builder); for (size_t i = 0; i < inputs.size(); ++i) { auto funcInputVal = func.getArgument(i); @@ -170,7 +158,7 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, auto tensorTy = mlir::RankedTensorType::get(ranked.getShape(), ranked.getElementType()); auto tensor = block_builder.create( loc, tensorTy, funcInputVal, /*restrict = */ true, /*writable=*/ true); - conversion_context.nodeOutputMap.emplace(inputs[i], tensor); + graph_converter.nodeOutputMap.emplace(inputs[i], tensor); // FIXME: Avoid pre-population of dimension_map, take dimension values only if needed auto input_shape = inputs[i].get_partial_shape(); @@ -182,9 +170,9 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, auto symbol = dim.get_symbol(); assert(symbol); symbol = ov::symbol::ancestor_of(symbol); - if(dim.is_dynamic() && !conversion_context.dimension_map.count(symbol)) { + if(dim.is_dynamic() && !graph_converter.dimension_map.count(symbol)) { auto dimSize = block_builder.create(loc, tensor, j); - conversion_context.dimension_map[symbol] = dimSize; + graph_converter.dimension_map[symbol] = dimSize; } } } @@ -193,14 +181,14 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, for (size_t i = 0; i < nodes.size(); ++i) { auto node = nodes[i]; - conversion_context.convert(node); + graph_converter.convert(node); } SmallVector funcOutputs; funcOutputs.reserve(outputs.size()); for (size_t i = 0; i < outputs.size(); ++i) { - auto tensor = conversion_context.nodeOutputMap.at(outputs[i]); + auto tensor = graph_converter.nodeOutputMap.at(outputs[i]); auto memref = func.getArgument(i + inputs.size()); auto loc = createLocation(context, outputs[i].get_node_shared_ptr()); // Ensure the result is stored in the provided function argument. diff --git a/src/common/transformations/src/transformations/mlir/conversion_context.cpp b/src/common/transformations/src/transformations/mlir/graph_converter.cpp similarity index 67% rename from src/common/transformations/src/transformations/mlir/conversion_context.cpp rename to src/common/transformations/src/transformations/mlir/graph_converter.cpp index 89692ae2bc80d7..bbf923d9d24b31 100644 --- a/src/common/transformations/src/transformations/mlir/conversion_context.cpp +++ b/src/common/transformations/src/transformations/mlir/graph_converter.cpp @@ -5,7 +5,8 @@ // #include "mlir/IR/BuiltinAttributes.h" // #include "mlir/IR/BuiltinTypes.h" -#include "conversion_context.hpp" +#include "common/conversion_context.hpp" +#include "graph_converter.hpp" namespace ov { @@ -14,16 +15,15 @@ namespace mlir { using namespace ::mlir; -std::string ConversionContext::rt_info_convertor () { +std::string GraphConverter::rt_info_convertor () { return "__mlir_convertor"; } -ConversionContext::ConversionContext(mlir::MLIRContext* context, mlir::OpBuilder* block_builder) - : context(context), - block_builder(block_builder) {} +GraphConverter::GraphConverter(mlir::MLIRContext* context, mlir::OpBuilder* block_builder) + : _ctx(context, block_builder, [this](NodePtr node) { return getInputs(node); }, [this](const Dimension& dim) { return get_dimension_value(dim); }) {} -SmallVector ConversionContext::getInputs(NodePtr node) { +SmallVector GraphConverter::getInputs(NodePtr node) { SmallVector out; out.reserve(node->get_input_size()); for (const auto& input : node->inputs()) { @@ -32,7 +32,7 @@ SmallVector ConversionContext::getInputs(NodePtr node) { return out; } -void ConversionContext::addOutputs(NodePtr node, mlir::Operation* op) { +void GraphConverter::addOutputs(NodePtr node, mlir::Operation* op) { const auto results = op->getOpResults(); OPENVINO_ASSERT( @@ -47,18 +47,19 @@ void ConversionContext::addOutputs(NodePtr node, mlir::Operation* op) { } } -void ConversionContext::convert(NodePtr node) { +void GraphConverter::convert(NodePtr node) { auto convertor = node->get_rt_info()[rt_info_convertor()].as(); - convertor(*this, node); + auto mlirOp = convertor(_ctx, node); + addOutputs(node, mlirOp); } -void ConversionContext::set_convertor(NodePtr node, const Convertor& convertor) { +void GraphConverter::set_convertor(NodePtr node, const Convertor& convertor) { Convertor local_copy = convertor; auto as_any = ov::Any(local_copy); node->get_rt_info()[rt_info_convertor()] = as_any; } -Value ConversionContext::get_dimension_value(const Dimension& d) { +Value GraphConverter::get_dimension_value(const Dimension& d) { auto symbol = d.get_symbol(); assert(symbol); symbol = ov::symbol::ancestor_of(symbol); @@ -68,16 +69,6 @@ Value ConversionContext::get_dimension_value(const Dimension& d) { return dimension_map.at(symbol); } -SmallVector ConversionContext::get_dynamic_dimension_values (const PartialShape& shape) { - SmallVector dims; - for (const auto& dim: shape) { - if (dim.is_dynamic()) { - dims.push_back(get_dimension_value(dim)); - } - } - return dims; -} - const std::string& subgraph_mark() { static const std::string mark = "__subgraph_mlir_mark"; @@ -93,12 +84,12 @@ bool get_subgraph_mark(NodePtr node) { } -MarkPattern::MarkPattern(NodePtr pattern, ConversionContext::Convertor convertor) { +MarkPattern::MarkPattern(NodePtr pattern, GraphConverter::Convertor convertor) { auto callback = [convertor](ov::pass::pattern::Matcher& m) { // TODO: support multi-node patterns marking auto node = m.get_match_root(); set_subgraph_mark(node); - ConversionContext::set_convertor(node, convertor); + GraphConverter::set_convertor(node, convertor); return true; }; diff --git a/src/common/transformations/src/transformations/mlir/conversion_context.hpp b/src/common/transformations/src/transformations/mlir/graph_converter.hpp similarity index 69% rename from src/common/transformations/src/transformations/mlir/conversion_context.hpp rename to src/common/transformations/src/transformations/mlir/graph_converter.hpp index 314b0529642453..95f6b1524fc7a8 100644 --- a/src/common/transformations/src/transformations/mlir/conversion_context.hpp +++ b/src/common/transformations/src/transformations/mlir/graph_converter.hpp @@ -10,8 +10,9 @@ #include "mlir/IR/MLIRContext.h" #include "mlir/IR/Builders.h" -#include "typedefs.hpp" -#include "convert_common.hpp" +#include "common/typedefs.hpp" +#include "common/convert_common.hpp" +#include "common/conversion_context.hpp" namespace ov { namespace mlir { @@ -23,35 +24,28 @@ using ::mlir::Operation; using ::mlir::SmallVector; using ::mlir::ValueRange; -class ConversionContext { +class GraphConverter { static std::string rt_info_convertor (); + ConversionContext _ctx; public: - using Convertor = std::function; + using Convertor = std::function; using NodeOutputMap = std::map, mlir::Value>; static const std::map convertors; - mlir::MLIRContext* context; - mlir::OpBuilder* block_builder; NodeOutputMap nodeOutputMap; std::map dimension_map; - ConversionContext(mlir::MLIRContext* context, mlir::OpBuilder* block_builder); + GraphConverter(mlir::MLIRContext* context, mlir::OpBuilder* block_builder); SmallVector getInputs(NodePtr node); void addOutputs(NodePtr node, mlir::Operation* op); - mlir::OpBuilder& builder() { - return *block_builder; - } - static void set_convertor(NodePtr node, const Convertor& convertor); void convert(NodePtr node); Value get_dimension_value(const Dimension& d); - - SmallVector get_dynamic_dimension_values (const PartialShape& shape); }; @@ -64,7 +58,7 @@ bool get_subgraph_mark(NodePtr node); class MarkPattern : public ov::pass::MatcherPass { public: OPENVINO_RTTI("MarkPattern", "0"); - MarkPattern(NodePtr pattern, ConversionContext::Convertor convertor); + MarkPattern(NodePtr pattern, GraphConverter::Convertor convertor); }; } // namespace mlir diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.hpp b/src/common/transformations/src/transformations/mlir/mlir_op.hpp index 918ab4a7270024..e08072e2306125 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.hpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.hpp @@ -13,7 +13,7 @@ #include "openvino/op/op.hpp" #include "openvino/core/shape.hpp" -#include "convert_common.hpp" +#include "common/convert_common.hpp" #ifdef GC_USE_GPU // GC_GPU requires IMEX support #include "gc/Utils/Error.h" diff --git a/src/common/transformations/src/transformations/mlir/op/binary_eltwise.cpp b/src/common/transformations/src/transformations/mlir/op/binary_eltwise.cpp deleted file mode 100644 index c840c720b4f497..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/binary_eltwise.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Value.h" -#include "mlir/Dialect/Tensor/IR/Tensor.h" -#include "mlir/Dialect/Linalg/Passes.h" - -#include -#include "openvino/pass/pattern/op/wrap_type.hpp" - -#include "binary_eltwise.hpp" - -namespace { - -using namespace ov; -using namespace ov::mlir; -using ::mlir::ValueRange; - -class ConvertBinaryEltwise { - - BinaryEltwisePatternBase::Builder m_op_builder; - -public: - - ConvertBinaryEltwise(BinaryEltwisePatternBase::Builder op_builder) : m_op_builder(op_builder) {} - - void operator()(ConversionContext& context, NodePtr node) { - auto loc = createLocation(context.context, node); - auto& builder = context.builder(); - const auto inputs = context.getInputs(node); - const auto ov_output_element_type = node->get_output_element_type(0); - const auto ov_output_shape = node->get_output_partial_shape(0); - auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); - const int output_rank = ov_output_shape.rank().get_length(); - - SmallVector dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); - - SmallVector broadcasted_inputs; - for(size_t i = 0; i < inputs.size(); ++i) { - auto [collapse_groups, dimensions] = broadcast_dimensions(node->get_input_partial_shape(i), ov_output_shape); - if(!dimensions.empty()) { - // FIXME: Find a way to avoid dimension squeezing before applying linalg.broadcast - // Step 1: Squeeze input shape to eliminate broadcasted dimensions - auto squeezed = builder.create(loc, inputs[i], collapse_groups); - // Step 2: Broadcast squeezed shape to the target shape - auto empty = builder.create(loc, outType, dynamic_dimensions); - auto op = builder.create(loc, squeezed, empty, dimensions); - broadcasted_inputs.push_back(op.getResult()[0]); - } else { - broadcasted_inputs.push_back(inputs[i]); - } - } - - auto empty = builder.create(loc, outType, dynamic_dimensions); - auto op = m_op_builder(builder, loc, ValueRange(broadcasted_inputs), ValueRange{empty}); - context.addOutputs(node, op); - } -}; - -} // namespace - -namespace ov { -namespace mlir { - -using namespace ov::pass::pattern; - -BinaryEltwisePatternBase::BinaryEltwisePatternBase(NodeTypeInfo wrapped_type, Builder op_builder, const std::set& element_types) - : MarkPattern( - std::make_shared( - wrapped_type, - [element_types](const Output& output) { - if(!element_types.empty() && !element_types.count(output.get_element_type())) { - return false; - } - auto node = output.get_node_shared_ptr(); - for(const auto& input: node->inputs()) { - if(!statically_broadcastable(input.get_partial_shape(), output.get_partial_shape())) { - return false; - } - } - return true; - }, - OutputVector{any_input(), any_input()}), - ConvertBinaryEltwise(op_builder)) - {} - -} // namespace mlir -} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/binary_eltwise.hpp b/src/common/transformations/src/transformations/mlir/op/binary_eltwise.hpp deleted file mode 100644 index 1c410608cbc4a5..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/binary_eltwise.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class BinaryEltwisePatternBase : public MarkPattern { -public: - using Builder = std::function; - - OPENVINO_RTTI("BinaryEltwisePatternBase", "0"); - BinaryEltwisePatternBase(NodeTypeInfo wrapped_type, Builder op_builder, const std::set& element_types = {}); -}; - - -template -class BinaryEltwisePattern : public BinaryEltwisePatternBase { -public: - // Allow conversion for given `element_types` only, except case when `element_types` is empty which means no restrictions on types, everything is allowed. - BinaryEltwisePattern (const std::set& element_types = {}) : - BinaryEltwisePatternBase( - OVOp::get_type_info_static(), - [](OpBuilder& builder, ::mlir::Location loc, ValueRange ins, ValueRange outs) -> Operation* { - return builder.create(loc, ins, outs); - }, - element_types) - {} - - BinaryEltwisePattern (const element::Type& element_type) : - BinaryEltwisePattern(std::set{element_type}) - {} -}; - - -} // namespace mlir -} // namespace ov - diff --git a/src/common/transformations/src/transformations/mlir/op/concat.hpp b/src/common/transformations/src/transformations/mlir/op/concat.hpp deleted file mode 100644 index bd1dfece8634f6..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/concat.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Value.h" - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class ConcatPattern : public MarkPattern { -public: - OPENVINO_RTTI("ConcatPattern", "0"); - ConcatPattern(); -}; - -} // namespace mlir -} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/floor.hpp b/src/common/transformations/src/transformations/mlir/op/floor.hpp deleted file mode 100644 index 860818cb727a06..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/floor.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Value.h" - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class FloorPattern : public MarkPattern { -public: - OPENVINO_RTTI("FloorPattern", "0"); - FloorPattern(); -}; - -} // namespace mlir -} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/gather.hpp b/src/common/transformations/src/transformations/mlir/op/gather.hpp deleted file mode 100644 index 8746136730526d..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/gather.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Value.h" - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class GatherPattern : public MarkPattern { -public: - OPENVINO_RTTI("GatherPattern", "0"); - GatherPattern(); -}; - -} // namespace mlir -} // namespace ov - diff --git a/src/common/transformations/src/transformations/mlir/op/matmul.hpp b/src/common/transformations/src/transformations/mlir/op/matmul.hpp deleted file mode 100644 index ec326af6cb496f..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/matmul.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/Value.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Builders.h" - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class MatMulPattern : public MarkPattern { -public: - OPENVINO_RTTI("MatMulPattern", "0"); - MatMulPattern(); -}; - - -} // namespace mlir -} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/op/reduce.hpp b/src/common/transformations/src/transformations/mlir/op/reduce.hpp deleted file mode 100644 index 2e1d592a02f32b..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/reduce.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -template -class ReducePattern : public MarkPattern { -public: - OPENVINO_RTTI("ReducePattern", "0"); - ReducePattern(); -}; - -} // namespace mlir -} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/relu.hpp b/src/common/transformations/src/transformations/mlir/op/relu.hpp deleted file mode 100644 index a51c7366d834fb..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/relu.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Value.h" - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class ReluPattern : public MarkPattern { -public: - OPENVINO_RTTI("ReluPattern", "0"); - ReluPattern(); -}; - -} // namespace mlir -} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/sdpa.hpp b/src/common/transformations/src/transformations/mlir/op/sdpa.hpp deleted file mode 100644 index 1d38d1af3e99a7..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/sdpa.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Value.h" - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class SDPAPattern : public MarkPattern { -public: - OPENVINO_RTTI("SDPAPattern", "0"); - SDPAPattern(); -}; - -} // namespace mlir -} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/shape_of.hpp b/src/common/transformations/src/transformations/mlir/op/shape_of.hpp deleted file mode 100644 index 1915004057695f..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/shape_of.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Value.h" - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class ShapeOfPattern : public MarkPattern { -public: - OPENVINO_RTTI("ShapeOfPattern", "0"); - ShapeOfPattern(); -}; - -} // namespace mlir -} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/slice.hpp b/src/common/transformations/src/transformations/mlir/op/slice.hpp deleted file mode 100644 index b4ba5a9cdca645..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/slice.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Value.h" - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class SlicePattern : public MarkPattern { -public: - OPENVINO_RTTI("SlicePattern", "0"); - SlicePattern(); -}; - -} // namespace mlir -} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/squeeze.hpp b/src/common/transformations/src/transformations/mlir/op/squeeze.hpp deleted file mode 100644 index 57d064e4c112ea..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/squeeze.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Value.h" - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class SqueezePattern : public MarkPattern { -public: - OPENVINO_RTTI("SqueezePattern", "0"); - SqueezePattern(); -}; - -} // namespace mlir -} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/transpose.hpp b/src/common/transformations/src/transformations/mlir/op/transpose.hpp deleted file mode 100644 index 22de4ddf0b4935..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/transpose.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Value.h" - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class TransposePattern : public MarkPattern { -public: - OPENVINO_RTTI("TransposePattern", "0"); - TransposePattern(); -}; - -} // namespace mlir -} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/op/unsqueeze.hpp b/src/common/transformations/src/transformations/mlir/op/unsqueeze.hpp deleted file mode 100644 index 21d4eb44b53b52..00000000000000 --- a/src/common/transformations/src/transformations/mlir/op/unsqueeze.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Value.h" - -#include "../conversion_context.hpp" - -namespace ov { -namespace mlir { - -class UnsqueezePattern : public MarkPattern { -public: - OPENVINO_RTTI("UnsqueezePattern", "0"); - UnsqueezePattern(); -}; - -} // namespace mlir -} // namespace ov - diff --git a/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp b/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp index 19abadcd602f0b..b43130ded11048 100644 --- a/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp +++ b/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp @@ -6,7 +6,7 @@ #include -#include "typedefs.hpp" +#include "common/typedefs.hpp" namespace ov { From d305154cdb077040c1fa1332bd9cca270a53a8d3 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Fri, 24 Apr 2026 19:41:57 +0000 Subject: [PATCH 061/121] Drop unused input args --- .../src/transformations/mlir/convert.cpp | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index 7ac30b9e3e0ce9..b7ed85d268cb14 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -118,11 +118,33 @@ SmallVector get_types_for_values(mlir::MLIRContext* context, const o return types; } +// Erases input function args that have no uses. +void dropUnusedInputArgs(mlir::func::FuncOp func, size_t numInputs, SmallVector& kept) { + llvm::BitVector toErase(func.getNumArguments()); + for (size_t i = 0; i < numInputs; ++i) { + auto arg = func.getArgument(i); + if (arg.use_empty()) { + toErase.set(i); + continue; + } + if (arg.hasOneUse()) { + auto toTensor = mlir::dyn_cast(*arg.user_begin()); + if (toTensor && toTensor.getResult().use_empty()) { + toTensor.erase(); + toErase.set(i); + continue; + } + } + kept.push_back(i); + } + func.eraseArguments(toErase); +} mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, const ov::OutputVector& inputs, const ov::NodeVector& nodes, - const ov::OutputVector& outputs) { + const ov::OutputVector& outputs, + SmallVector& keptInputIndices) { auto inputTypes = tensorsToMemRefs(get_types_for_values(context, inputs)); auto outputTypes = tensorsToMemRefs(get_types_for_values(context, outputs)); @@ -204,19 +226,24 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, const auto retLoc = createLayerLocation(context, "output", "Output"); block_builder.create(retLoc, ArrayRef(SmallVector())); - + dropUnusedInputArgs(func, inputs.size(), keptInputIndices); return module; } - // This pass converts a group of nodes into a single MLIROp NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph, MlirMode mode, std::shared_ptr loweringContext) { - mlir::OwningOpRef module = ngraph_to_mlir(context, subgraph->inputs, subgraph->nodes, subgraph->outputs); - - const auto& inputs = subgraph->inputs; + SmallVector keptInputIndices; + mlir::OwningOpRef module = + ngraph_to_mlir(context, subgraph->inputs, subgraph->nodes, subgraph->outputs, keptInputIndices); + + ov::OutputVector inputs; + inputs.reserve(keptInputIndices.size()); + for (size_t idx : keptInputIndices) { + inputs.push_back(subgraph->inputs[idx]); + } using Index = DimensionsMap::value_type::value_type; std::map input_map; for (size_t i = 0; i < inputs.size(); ++i) { @@ -258,7 +285,7 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, output_map.emplace_back(dm); } return std::make_shared( - subgraph->inputs, + inputs, MLIREvaluate::create(std::move(module), mode, loweringContext), output_types, output_map From 79935c36ef9484cdf220f91e7a80518651c66a16 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Fri, 24 Apr 2026 19:49:48 +0000 Subject: [PATCH 062/121] Added ReduceMeanTest --- .../functional/single_layer_tests/reduce.cpp | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp index cd011d36c9537a..6c63eb7344d836 100644 --- a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp +++ b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp @@ -2,15 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 // +#include "common_test_utils/file_utils.hpp" #include "common_test_utils/ov_tensor_utils.hpp" #include "common_test_utils/file_utils.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" #include "openvino/op/parameter.hpp" #include "openvino/op/constant.hpp" #include "openvino/op/reduce_sum.hpp" +#include "openvino/op/reduce_mean.hpp" #include "openvino/op/add.hpp" #include "openvino/op/multiply.hpp" #include "openvino/runtime/intel_gpu/properties.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" namespace { @@ -100,4 +103,45 @@ INSTANTIATE_TEST_SUITE_P( ::testing::Values(false)), ReduceSumSqueezeTest::getTestCaseName); -} // namespace +using ReduceMeanParams = std::tuple, // Reduce axes + bool>; // Keep dims + +class ReduceMeanTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [input_shape, precision, axes, keep_dims] = obj.param; + std::ostringstream result; + result << "IS=" << ov::test::utils::vec2str(input_shape) << "_"; + result << "axes=" << ov::test::utils::vec2str(axes) << "_"; + result << "keep_dims=" << keep_dims << "_"; + result << "precision=" << precision; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto& [input_shape, precision, axes, keep_dims] = GetParam(); + auto input = std::make_shared(precision, input_shape); + auto axes_node = ov::op::v0::Constant::create(ov::element::i64, {axes.size()}, axes); + auto reduce = std::make_shared(input, axes_node, keep_dims); + auto result = std::make_shared(reduce); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input}, "ReduceMean"); + } +}; + +TEST_P(ReduceMeanTest, Inference) { + run(); +} + +INSTANTIATE_TEST_SUITE_P(smoke_ReduceMeanTest, + ReduceMeanTest, + ::testing::Combine(::testing::Values(ov::Shape{1, 24, 1024, 64}), + ::testing::Values(ov::element::f32), + ::testing::Values(std::vector{3}), + ::testing::Values(true)), + ReduceMeanTest::getTestCaseName); + +} // namespace From 51ede59505224c676412a889c5acce7b88713e55 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Wed, 29 Apr 2026 13:50:18 +0000 Subject: [PATCH 063/121] Added tests for all supported Reduce* ops --- .../functional/single_layer_tests/reduce.cpp | 56 ++++++++++++------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp index 6c63eb7344d836..aa02a4da34b05e 100644 --- a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp +++ b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp @@ -8,8 +8,11 @@ #include "shared_test_classes/base/ov_subgraph.hpp" #include "openvino/op/parameter.hpp" #include "openvino/op/constant.hpp" -#include "openvino/op/reduce_sum.hpp" +#include "openvino/op/reduce_max.hpp" #include "openvino/op/reduce_mean.hpp" +#include "openvino/op/reduce_min.hpp" +#include "openvino/op/reduce_prod.hpp" +#include "openvino/op/reduce_sum.hpp" #include "openvino/op/add.hpp" #include "openvino/op/multiply.hpp" #include "openvino/runtime/intel_gpu/properties.hpp" @@ -103,14 +106,15 @@ INSTANTIATE_TEST_SUITE_P( ::testing::Values(false)), ReduceSumSqueezeTest::getTestCaseName); -using ReduceMeanParams = std::tuple, // Reduce axes - bool>; // Keep dims +using ReduceParams = std::tuple, // Reduce axes + bool>; // Keep dims -class ReduceMeanTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +template +class ReduceTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { public: - static std::string getTestCaseName(const testing::TestParamInfo& obj) { + static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [input_shape, precision, axes, keep_dims] = obj.param; std::ostringstream result; result << "IS=" << ov::test::utils::vec2str(input_shape) << "_"; @@ -126,22 +130,34 @@ class ReduceMeanTest : public testing::WithParamInterface, vir const auto& [input_shape, precision, axes, keep_dims] = GetParam(); auto input = std::make_shared(precision, input_shape); auto axes_node = ov::op::v0::Constant::create(ov::element::i64, {axes.size()}, axes); - auto reduce = std::make_shared(input, axes_node, keep_dims); + auto reduce = std::make_shared(input, axes_node, keep_dims); auto result = std::make_shared(reduce); - function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input}, "ReduceMean"); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input}, "Reduce"); } }; -TEST_P(ReduceMeanTest, Inference) { - run(); -} - -INSTANTIATE_TEST_SUITE_P(smoke_ReduceMeanTest, - ReduceMeanTest, - ::testing::Combine(::testing::Values(ov::Shape{1, 24, 1024, 64}), - ::testing::Values(ov::element::f32), - ::testing::Values(std::vector{3}), - ::testing::Values(true)), - ReduceMeanTest::getTestCaseName); +using ReduceMeanTest = ReduceTest; +using ReduceMaxTest = ReduceTest; +using ReduceMinTest = ReduceTest; +using ReduceProdTest = ReduceTest; +using ReduceSumTest = ReduceTest; + +TEST_P(ReduceMeanTest, Inference) { run(); } +TEST_P(ReduceMaxTest, Inference) { run(); } +TEST_P(ReduceMinTest, Inference) { run(); } +TEST_P(ReduceProdTest, Inference) { run(); } +TEST_P(ReduceSumTest, Inference) { run(); } + +const auto reduce_test_params = ::testing::Combine(::testing::Values(ov::Shape{1, 24, 1024, 64}), + ::testing::Values(ov::element::f32), + ::testing::Values(std::vector{3}), + ::testing::Values(true)); +// ::testing::Values(true, false)); + +INSTANTIATE_TEST_SUITE_P(smoke_ReduceMeanTest, ReduceMeanTest, reduce_test_params, ReduceMeanTest::getTestCaseName); +INSTANTIATE_TEST_SUITE_P(smoke_ReduceMaxTest, ReduceMaxTest, reduce_test_params, ReduceMaxTest::getTestCaseName); +INSTANTIATE_TEST_SUITE_P(smoke_ReduceMinTest, ReduceMinTest, reduce_test_params, ReduceMinTest::getTestCaseName); +INSTANTIATE_TEST_SUITE_P(smoke_ReduceProdTest, ReduceProdTest, reduce_test_params, ReduceProdTest::getTestCaseName); +INSTANTIATE_TEST_SUITE_P(smoke_ReduceSumTest, ReduceSumTest, reduce_test_params, ReduceSumTest::getTestCaseName); } // namespace From c4570016029a1b2b091a3756469789ddaf5c3962 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Tue, 12 May 2026 14:06:42 +0200 Subject: [PATCH 064/121] Added binary elementwise tests (#116) - Added tests for all supported bin elementwise ops. - Inline OV constants, passed ass the kernel args. --- .../include/transformations/mlir/convert.hpp | 5 + .../mlir/common/convert_common.hpp | 20 ++ .../src/transformations/mlir/convert.cpp | 76 +++++--- .../src/plugin/transformations_pipeline.cpp | 5 + .../functional/mlir_op/binary_eltwise.cpp | 174 ++++++++++++++++++ 5 files changed, 254 insertions(+), 26 deletions(-) create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp diff --git a/src/common/transformations/include/transformations/mlir/convert.hpp b/src/common/transformations/include/transformations/mlir/convert.hpp index aed271d1baf38a..700e27a4e34898 100644 --- a/src/common/transformations/include/transformations/mlir/convert.hpp +++ b/src/common/transformations/include/transformations/mlir/convert.hpp @@ -5,12 +5,17 @@ #pragma once #include "openvino/core/model.hpp" +#include "openvino/util/env_util.hpp" #include "transformations_visibility.hpp" namespace ov { namespace pass { +inline bool is_mlir_transform_enabled() { + return !util::getenv_string("OV_MLIR_MODE").empty() && util::getenv_bool("OV_MLIR", true); +} + void TRANSFORMATIONS_API transformMLIR(std::shared_ptr model, std::shared_ptr loweringContext); diff --git a/src/common/transformations/src/transformations/mlir/common/convert_common.hpp b/src/common/transformations/src/transformations/mlir/common/convert_common.hpp index 9d1fd473d640b9..8936e28181d6a0 100644 --- a/src/common/transformations/src/transformations/mlir/common/convert_common.hpp +++ b/src/common/transformations/src/transformations/mlir/common/convert_common.hpp @@ -13,6 +13,8 @@ #include "mlir/Dialect/Arith/IR/Arith.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "openvino/op/constant.hpp" + #include "typedefs.hpp" @@ -61,6 +63,24 @@ mlir::arith::ConstantOp getConstant(OpBuilder& builder, return getConstant(builder, importPrecision(builder.getContext(), precision), value, loc); } +inline arith::ConstantOp getConstant(OpBuilder& builder, + const ov::op::v0::Constant* constant, + std::optional loc = std::nullopt) { + auto tensorTy = dyn_cast( + importTensor(builder.getContext(), constant->get_output_partial_shape(0), constant->get_element_type())); + DenseElementsAttr attr; + if (constant->get_all_data_elements_bitwise_identical()) { + auto raw = llvm::ArrayRef(static_cast(constant->get_data_ptr()), + constant->get_element_type().size()); + auto scalarAttr = DenseElementsAttr::getFromRawBuffer(tensorTy.cloneWith({}, tensorTy.getElementType()), raw); + attr = DenseElementsAttr::get(tensorTy, scalarAttr.getSplatValue()); + } else { + auto raw = llvm::ArrayRef(static_cast(constant->get_data_ptr()), constant->get_byte_size()); + attr = DenseElementsAttr::getFromRawBuffer(tensorTy, raw); + } + return arith::ConstantOp::create(builder, loc.value_or(builder.getUnknownLoc()), tensorTy, attr); +} + using BroadcastDimensions = std::tuple, SmallVector>; BroadcastDimensions broadcast_dimensions(const PartialShape& from, const PartialShape& to); diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index b7ed85d268cb14..d90f864a4b2231 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -145,7 +146,19 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, const ov::NodeVector& nodes, const ov::OutputVector& outputs, SmallVector& keptInputIndices) { - auto inputTypes = tensorsToMemRefs(get_types_for_values(context, inputs)); + // Split inputs: splat constants are inlined, runtime inputs become function args. + ov::OutputVector runtime_inputs; + SmallVector is_constant(inputs.size(), false); + for (size_t i = 0; i < inputs.size(); ++i) { + auto* c = ov::as_type(inputs[i].get_node()); + if (c && c->get_all_data_elements_bitwise_identical()) { + is_constant[i] = true; + } else { + runtime_inputs.push_back(inputs[i]); + } + } + + auto inputTypes = tensorsToMemRefs(get_types_for_values(context, runtime_inputs)); auto outputTypes = tensorsToMemRefs(get_types_for_values(context, outputs)); const auto moduleLoc = createLayerLocation(context, "module", "Module"); @@ -172,29 +185,34 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, GraphConverter graph_converter(context, &block_builder); - for (size_t i = 0; i < inputs.size(); ++i) { - auto funcInputVal = func.getArgument(i); - // transition from memref enclosure to tensor interior + for (size_t i = 0, r = 0; i < inputs.size(); ++i) { auto loc = createLocation(context, inputs[i].get_node_shared_ptr()); - auto ranked = mlir::dyn_cast(funcInputVal.getType()); - auto tensorTy = mlir::RankedTensorType::get(ranked.getShape(), ranked.getElementType()); - auto tensor = block_builder.create( - loc, tensorTy, funcInputVal, /*restrict = */ true, /*writable=*/ true); - graph_converter.nodeOutputMap.emplace(inputs[i], tensor); - - // FIXME: Avoid pre-population of dimension_map, take dimension values only if needed - auto input_shape = inputs[i].get_partial_shape(); - auto input_rank = input_shape.rank(); - if(input_rank.is_static()) { - for(size_t j = 0; j < input_rank.get_length(); ++j) { - auto dim = input_shape[j]; - if(dim.is_dynamic()) { - auto symbol = dim.get_symbol(); - assert(symbol); - symbol = ov::symbol::ancestor_of(symbol); - if(dim.is_dynamic() && !graph_converter.dimension_map.count(symbol)) { - auto dimSize = block_builder.create(loc, tensor, j); - graph_converter.dimension_map[symbol] = dimSize; + if (is_constant[i]) { + auto* cst = ov::as_type(inputs[i].get_node()); + graph_converter.nodeOutputMap.emplace(inputs[i], getConstant(block_builder, cst, loc)); + } else { + auto funcInputVal = func.getArgument(r++); + // transition from memref enclosure to tensor interior + auto ranked = mlir::dyn_cast(funcInputVal.getType()); + auto tensorTy = mlir::RankedTensorType::get(ranked.getShape(), ranked.getElementType()); + auto tensor = block_builder.create( + loc, tensorTy, funcInputVal, /*restrict = */ true, /*writable=*/ true); + graph_converter.nodeOutputMap.emplace(inputs[i], tensor); + + // FIXME: Avoid pre-population of dimension_map, take dimension values only if needed + auto input_shape = inputs[i].get_partial_shape(); + auto input_rank = input_shape.rank(); + if(input_rank.is_static()) { + for(size_t j = 0; j < input_rank.get_length(); ++j) { + auto dim = input_shape[j]; + if(dim.is_dynamic()) { + auto symbol = dim.get_symbol(); + assert(symbol); + symbol = ov::symbol::ancestor_of(symbol); + if(dim.is_dynamic() && !graph_converter.dimension_map.count(symbol)) { + auto dimSize = block_builder.create(loc, tensor, j); + graph_converter.dimension_map[symbol] = dimSize; + } } } } @@ -211,7 +229,7 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, for (size_t i = 0; i < outputs.size(); ++i) { auto tensor = graph_converter.nodeOutputMap.at(outputs[i]); - auto memref = func.getArgument(i + inputs.size()); + auto memref = func.getArgument(i + runtime_inputs.size()); auto loc = createLocation(context, outputs[i].get_node_shared_ptr()); // Ensure the result is stored in the provided function argument. // Mark as restrict to avoid temporary buffer and copy. @@ -226,7 +244,13 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, const auto retLoc = createLayerLocation(context, "output", "Output"); block_builder.create(retLoc, ArrayRef(SmallVector())); - dropUnusedInputArgs(func, inputs.size(), keptInputIndices); + SmallVector keptRuntimeIndices; + dropUnusedInputArgs(func, runtime_inputs.size(), keptRuntimeIndices); + auto runtime_to_original = llvm::map_to_vector( + llvm::make_filter_range(llvm::enumerate(is_constant), [](const auto& p) { return !p.value(); }), + [](const auto& p) { return p.index(); }); + llvm::transform(keptRuntimeIndices, std::back_inserter(keptInputIndices), + [&](size_t i) { return runtime_to_original[i]; }); return module; } @@ -443,7 +467,7 @@ MLIRContext* get_shared_mlir_context(MlirMode mode) { void ov::pass::transformMLIR(std::shared_ptr model, std::shared_ptr loweringContext) { - if(util::getenv_bool("OV_MLIR", true)) { + if(is_mlir_transform_enabled()) { const char *default_mode = #ifdef TPP_MLIR "TPP"; diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index 8d948cfa1f0eb1..1b857a8c4c0c8f 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -592,6 +592,11 @@ void TransformationsPipeline::apply(std::shared_ptr func) { convert_input_output_precision, store_original_precision_as_rt_attribute); + if (ov::pass::is_mlir_transform_enabled()) { + pass_config->disable(); + pass_config->disable(); + } + manager.register_pass(); // In the case of "zp/scale -> reshape -> transpose -> MOE", diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp new file mode 100644 index 00000000000000..c5d5bd48a4345b --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp @@ -0,0 +1,174 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_test_utils/ov_tensor_utils.hpp" +#include "openvino/op/add.hpp" +#include "openvino/op/constant.hpp" +#include "openvino/op/divide.hpp" +#include "openvino/op/floor_mod.hpp" +#include "openvino/op/maximum.hpp" +#include "openvino/op/minimum.hpp" +#include "openvino/op/mod.hpp" +#include "openvino/op/multiply.hpp" +#include "openvino/op/parameter.hpp" +#include "openvino/op/power.hpp" +#include "openvino/op/result.hpp" +#include "openvino/op/subtract.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" + +namespace { + +// Params: lhs shape, rhs shape, precision +using BinaryElementwiseParams = std::tuple; + +template +class BinaryElementwiseTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [lhs_shape, rhs_shape, precision] = obj.param; + std::ostringstream result; + result << "LHS=" << ov::test::utils::vec2str(lhs_shape) << "_"; + result << "RHS=" << ov::test::utils::vec2str(rhs_shape) << "_"; + result << "precision=" << precision; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto& [lhs_shape, rhs_shape, precision] = GetParam(); + auto lhs = std::make_shared(precision, lhs_shape); + auto rhs = std::make_shared(precision, rhs_shape); + auto op = std::make_shared(lhs, rhs); + auto result = std::make_shared(op); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{lhs, rhs}); + } +}; + +using AddTest = BinaryElementwiseTest; +using SubtractTest = BinaryElementwiseTest; +using MultiplyTest = BinaryElementwiseTest; +using DivideTest = BinaryElementwiseTest; +using PowerTest = BinaryElementwiseTest; +using MaximumTest = BinaryElementwiseTest; +using MinimumTest = BinaryElementwiseTest; +using FloorModTest = BinaryElementwiseTest; +using ModTest = BinaryElementwiseTest; + +TEST_P(AddTest, Inference) { + run(); +} +TEST_P(SubtractTest, Inference) { + run(); +} +TEST_P(MultiplyTest, Inference) { + run(); +} +TEST_P(DivideTest, Inference) { + run(); +} +TEST_P(PowerTest, Inference) { + run(); +} +TEST_P(MaximumTest, Inference) { + run(); +} +TEST_P(MinimumTest, Inference) { + run(); +} +TEST_P(FloorModTest, Inference) { + run(); +} +TEST_P(ModTest, Inference) { + run(); +} + +// Same-shape +const auto same_shape_params = + ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 1536}), ::testing::Values(ov::Shape{1, 1024, 1536}), ::testing::Values(ov::element::f16)); +// Broadcast: arg0=[1, 1024, 1536], arg1=[1536] +const auto broadcast_params = + ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 1536}), ::testing::Values(ov::Shape{1536}), ::testing::Values(ov::element::f16)); + +#define INSTANTIATE_TS(Name) \ + INSTANTIATE_TEST_SUITE_P(smoke_BinaryElementwise##Name##_same_shape, Name, same_shape_params, Name::getTestCaseName); \ + INSTANTIATE_TEST_SUITE_P(smoke_BinaryElementwise##Name##_broadcast, Name, broadcast_params, Name::getTestCaseName) + +INSTANTIATE_TS(AddTest); +INSTANTIATE_TS(SubtractTest); +INSTANTIATE_TS(MultiplyTest); +INSTANTIATE_TS(DivideTest); +INSTANTIATE_TS(PowerTest); +INSTANTIATE_TS(MaximumTest); +INSTANTIATE_TS(MinimumTest); +INSTANTIATE_TS(FloorModTest); +INSTANTIATE_TS(ModTest); + +// Params: input shape, constant shape, precision +using BinaryElementwiseConstParams = std::tuple; + +template +class BinaryElementwiseConstTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [input_shape, const_shape, precision] = obj.param; + std::ostringstream result; + result << "Input=" << ov::test::utils::vec2str(input_shape) << "_"; + result << "Const=" << ov::test::utils::vec2str(const_shape) << "_"; + result << "precision=" << precision; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto& [input_shape, const_shape, precision] = GetParam(); + ov::test::utils::InputGenerateData gen; + gen.start_from = 3; + gen.range = 1; + auto const_tensor = ov::test::utils::create_and_fill_tensor(precision, const_shape, gen); + auto input = std::make_shared(precision, input_shape); + auto constant = std::make_shared(const_tensor); + auto op = std::make_shared(input, constant); + auto result = std::make_shared(op); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input}); + } +}; + +using AddConstTest = BinaryElementwiseConstTest; +using SubtractConstTest = BinaryElementwiseConstTest; +using MultiplyConstTest = BinaryElementwiseConstTest; +using DivideConstTest = BinaryElementwiseConstTest; + +TEST_P(AddConstTest, Inference) { + run(); +} +TEST_P(SubtractConstTest, Inference) { + run(); +} +TEST_P(MultiplyConstTest, Inference) { + run(); +} +TEST_P(DivideConstTest, Inference) { + run(); +} + +// Scalar constant broadcast +const auto const_scalar_params = + ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 1536}), ::testing::Values(ov::Shape{}), ::testing::Values(ov::element::f16)); +// 1D constant broadcast +const auto const_broadcast_params = + ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 1536}), ::testing::Values(ov::Shape{1536}), ::testing::Values(ov::element::f16)); + +#undef INSTANTIATE_TS +#define INSTANTIATE_TS(Name) \ + INSTANTIATE_TEST_SUITE_P(smoke_BinaryElementwise##Name##_scalar, Name, const_scalar_params, Name::getTestCaseName); \ + INSTANTIATE_TEST_SUITE_P(smoke_BinaryElementwise##Name##_broadcast, Name, const_broadcast_params, Name::getTestCaseName) + +INSTANTIATE_TS(AddConstTest); +INSTANTIATE_TS(SubtractConstTest); +INSTANTIATE_TS(MultiplyConstTest); +INSTANTIATE_TS(DivideConstTest); + +} // namespace From 9b1a4076f8788878daf4fe9c596e951e63d74ccc Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Tue, 12 May 2026 14:07:08 +0200 Subject: [PATCH 065/121] Implemented converters and tests for unary elementwise ops (#119) --- .../mlir/common/converters/unary_eltwise.hpp | 30 +++++++ .../mlir/conversion/patterns.cpp | 21 +++++ .../mlir/conversion/patterns.hpp | 6 ++ .../src/transformations/mlir/convert.cpp | 14 ++++ .../functional/mlir_op/unary_eltwise.cpp | 84 +++++++++++++++++++ 5 files changed, 155 insertions(+) create mode 100644 src/common/transformations/src/transformations/mlir/common/converters/unary_eltwise.hpp create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/unary_eltwise.hpp b/src/common/transformations/src/transformations/mlir/common/converters/unary_eltwise.hpp new file mode 100644 index 00000000000000..9745a1c752499b --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/common/converters/unary_eltwise.hpp @@ -0,0 +1,30 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "../convert_common.hpp" +#include "mlir/Dialect/Linalg/Passes.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" + +namespace ov { +namespace mlir { + +template +struct ConvertUnaryEltwise { + Operation* operator()(ConversionContext& context, NodePtr node) { + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto input = context.getInputs(node)[0]; + const auto ov_out_el_ty = node->get_output_element_type(0); + const auto ov_out_shape = node->get_output_partial_shape(0); + auto out_ty = importTensor(context.context, ov_out_shape, ov_out_el_ty); + auto dims = context.get_dynamic_dimension_values(ov_out_shape); + auto empty = tensor::EmptyOp::create(builder, loc, out_ty, dims); + return MlirUnaryOp::create(builder, loc, ::mlir::ValueRange{input}, ::mlir::ValueRange{empty}); + } +}; + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp b/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp index 89f9bf2c5a916f..429fc28f8e5e30 100644 --- a/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp +++ b/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp @@ -16,7 +16,14 @@ #include #include #include +#include +#include +#include +#include +#include #include +#include +#include #include #include #include @@ -27,6 +34,7 @@ #include "openvino/pass/pattern/op/wrap_type.hpp" #include "../common/converters/relu.hpp" +#include "../common/converters/unary_eltwise.hpp" #include "../common/converters/concat.hpp" #include "../common/converters/floor.hpp" #include "../common/converters/gather.hpp" @@ -129,5 +137,18 @@ template class BinaryEltwisePattern; template class BinaryEltwisePattern; template class BinaryEltwisePattern; +template +UnaryEltwisePattern::UnaryEltwisePattern() + : MarkPattern(wrap_type({any_input()}), ConvertUnaryEltwise()) {} + +// Explicit template instantiations +template class UnaryEltwisePattern; +template class UnaryEltwisePattern; +template class UnaryEltwisePattern; +template class UnaryEltwisePattern; +template class UnaryEltwisePattern; +template class UnaryEltwisePattern; +template class UnaryEltwisePattern; + } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp b/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp index 14bc1d0ed0978b..a7db53429253e3 100644 --- a/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp +++ b/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp @@ -102,5 +102,11 @@ class BinaryEltwisePattern : public BinaryEltwisePatternBase { : BinaryEltwisePattern(std::set{element_type}) {} }; +template +class UnaryEltwisePattern : public MarkPattern { +public: + UnaryEltwisePattern(); +}; + } // namespace mlir } // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index d90f864a4b2231..bc6d272b405b12 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -10,7 +10,14 @@ #include #include #include +#include +#include +#include +#include +#include #include +#include +#include #include #include #include @@ -379,6 +386,13 @@ void injectMLIR(std::shared_ptr model, manager.register_pass(); manager.register_pass(); manager.register_pass(); + manager.register_pass>(); + manager.register_pass>(); + manager.register_pass>(); + manager.register_pass>(); + manager.register_pass>(); + manager.register_pass>(); + manager.register_pass>(); manager.register_pass(); manager.register_pass(); manager.register_pass(); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp new file mode 100644 index 00000000000000..f7c82c86904d7b --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp @@ -0,0 +1,84 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_test_utils/ov_tensor_utils.hpp" +#include "openvino/op/abs.hpp" +#include "openvino/op/ceiling.hpp" +#include "openvino/op/exp.hpp" +#include "openvino/op/floor.hpp" +#include "openvino/op/log.hpp" +#include "openvino/op/negative.hpp" +#include "openvino/op/parameter.hpp" +#include "openvino/op/relu.hpp" +#include "openvino/op/result.hpp" +#include "openvino/op/sqrt.hpp" +#include "openvino/op/tanh.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" + +namespace { + +using UnaryElementwiseParams = std::tuple; + +template +class UnaryElementwiseTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [shape, precision] = obj.param; + std::ostringstream result; + result << "Input=" << ov::test::utils::vec2str(shape) << "_"; + result << "precision=" << precision; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto& [shape, precision] = GetParam(); + auto input = std::make_shared(precision, shape); + auto op = std::make_shared(input); + auto result = std::make_shared(op); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input}); + } +}; + +using AbsTest = UnaryElementwiseTest; +using CeilingTest = UnaryElementwiseTest; +using ExpTest = UnaryElementwiseTest; +using FloorTest = UnaryElementwiseTest; +using LogTest = UnaryElementwiseTest; +using NegativeTest = UnaryElementwiseTest; +using ReluTest = UnaryElementwiseTest; +using SqrtTest = UnaryElementwiseTest; +using TanhTest = UnaryElementwiseTest; + +#define DEFINE_TEST(Name) \ + TEST_P(Name, Inference) { \ + run(); \ + } + +DEFINE_TEST(AbsTest) +DEFINE_TEST(CeilingTest) +DEFINE_TEST(ExpTest) +DEFINE_TEST(FloorTest) +DEFINE_TEST(LogTest) +DEFINE_TEST(NegativeTest) +DEFINE_TEST(ReluTest) +DEFINE_TEST(SqrtTest) +DEFINE_TEST(TanhTest) + +const auto test_params = ::testing::Combine(::testing::Values(ov::Shape{1, 24, 1024, 1}, ov::Shape{1, 24, 128, 1}), ::testing::Values(ov::element::f16)); + +#define INSTANTIATE_TS(Name) INSTANTIATE_TEST_SUITE_P(smoke_UnaryElementwise##Name, Name, test_params, Name::getTestCaseName) + +INSTANTIATE_TS(AbsTest); +INSTANTIATE_TS(CeilingTest); +INSTANTIATE_TS(ExpTest); +INSTANTIATE_TS(FloorTest); +INSTANTIATE_TS(LogTest); +INSTANTIATE_TS(NegativeTest); +INSTANTIATE_TS(ReluTest); +INSTANTIATE_TS(SqrtTest); +INSTANTIATE_TS(TanhTest); + +} // namespace From 7addf0f9bddaac528477f000415f98c8f8d9aea3 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Wed, 13 May 2026 12:57:56 +0200 Subject: [PATCH 066/121] Added tests for the Transpose operation (#123) --- .../mlir/common/converters/transpose.hpp | 2 +- .../tests/functional/mlir_op/transpose.cpp | 51 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/transpose.hpp b/src/common/transformations/src/transformations/mlir/common/converters/transpose.hpp index c5efab33df90b0..ce031922f6d178 100644 --- a/src/common/transformations/src/transformations/mlir/common/converters/transpose.hpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/transpose.hpp @@ -30,7 +30,7 @@ struct ConvertTranspose { auto const_order = dynamic_cast(node->get_input_node_ptr(1)); assert(const_order && "non-const order not supported"); - ov::Coordinate coords = const_order->get_coordinate_val(); + auto coords = const_order->cast_vector(); SmallVector order(coords.begin(), coords.end()); auto empty = tensor::EmptyOp::create(builder, loc, out_type, dynamic_dimensions); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp new file mode 100644 index 00000000000000..157c1850c88ce0 --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp @@ -0,0 +1,51 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/op/transpose.hpp" + +#include "common_test_utils/ov_tensor_utils.hpp" +#include "openvino/op/constant.hpp" +#include "openvino/op/parameter.hpp" +#include "openvino/op/result.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" + +namespace { + +// Params: input shape, order, precision +using TransposeParams = std::tuple, ov::element::Type>; + +class TransposeTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [shape, order, precision] = obj.param; + std::ostringstream result; + result << "Input=" << ov::test::utils::vec2str(shape) << "_"; + result << "Order=" << ov::test::utils::vec2str(order) << "_"; + result << "precision=" << precision; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto& [shape, order, precision] = GetParam(); + auto input = std::make_shared(precision, shape); + auto order_const = ov::op::v0::Constant::create(ov::element::i64, {order.size()}, order); + auto op = std::make_shared(input, order_const); + auto result = std::make_shared(op); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input}); + } +}; + +TEST_P(TransposeTest, Inference) { + run(); +} + +const auto test_params = ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 24, 64}, ov::Shape{1, 128, 24, 64}), + ::testing::Values(std::vector{0, 2, 1, 3}), + ::testing::Values(ov::element::f16)); + +INSTANTIATE_TEST_SUITE_P(smoke_Transpose, TransposeTest, test_params, TransposeTest::getTestCaseName); + +} // namespace From c83e1b59a2ae3161f57a7bf44a8586b35fdaa261 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Wed, 13 May 2026 15:29:12 +0200 Subject: [PATCH 067/121] Moved reduction tests to mlir_op (#128) --- .../tests/functional/mlir_op/reduction.cpp | 83 +++++++++++++++++++ .../functional/single_layer_tests/reduce.cpp | 62 +------------- 2 files changed, 84 insertions(+), 61 deletions(-) create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp new file mode 100644 index 00000000000000..3140800a95b1b9 --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_test_utils/ov_tensor_utils.hpp" +#include "openvino/op/add.hpp" +#include "openvino/op/constant.hpp" +#include "openvino/op/multiply.hpp" +#include "openvino/op/parameter.hpp" +#include "openvino/op/reduce_max.hpp" +#include "openvino/op/reduce_mean.hpp" +#include "openvino/op/reduce_min.hpp" +#include "openvino/op/reduce_prod.hpp" +#include "openvino/op/reduce_sum.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" + +namespace { +using ReduceParams = std::tuple, // Reduce axes + bool>; // Keep dims + +template +class ReduceTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [input_shape, precision, axes, keep_dims] = obj.param; + std::ostringstream result; + result << "IS=" << ov::test::utils::vec2str(input_shape) << "_"; + result << "axes=" << ov::test::utils::vec2str(axes) << "_"; + result << "keep_dims=" << keep_dims << "_"; + result << "precision=" << precision; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto& [input_shape, precision, axes, keep_dims] = GetParam(); + auto input = std::make_shared(precision, input_shape); + auto axes_node = ov::op::v0::Constant::create(ov::element::i64, {axes.size()}, axes); + auto reduce = std::make_shared(input, axes_node, keep_dims); + auto result = std::make_shared(reduce); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input}, "Reduce"); + } +}; + +using ReduceMeanTest = ReduceTest; +using ReduceMaxTest = ReduceTest; +using ReduceMinTest = ReduceTest; +using ReduceProdTest = ReduceTest; +using ReduceSumTest = ReduceTest; + +TEST_P(ReduceMeanTest, Inference) { + run(); +} +TEST_P(ReduceMaxTest, Inference) { + run(); +} +TEST_P(ReduceMinTest, Inference) { + run(); +} +TEST_P(ReduceProdTest, Inference) { + run(); +} +TEST_P(ReduceSumTest, Inference) { + run(); +} + +const auto reduce_test_params = ::testing::Combine(::testing::Values(ov::Shape{1, 24, 1024, 64}), + ::testing::Values(ov::element::f32), + ::testing::Values(std::vector{3}), + ::testing::Values(true)); +// ::testing::Values(true, false)); + +#define INSTANTIATE_TS(Name) INSTANTIATE_TEST_SUITE_P(mlir_Reduce##Name##Test, Reduce##Name##Test, reduce_test_params, Reduce##Name##Test::getTestCaseName) +INSTANTIATE_TS(Mean); +INSTANTIATE_TS(Max); +INSTANTIATE_TS(Min); +INSTANTIATE_TS(Prod); +INSTANTIATE_TS(Sum); + +} // namespace diff --git a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp index aa02a4da34b05e..cd011d36c9537a 100644 --- a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp +++ b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp @@ -2,21 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "common_test_utils/file_utils.hpp" #include "common_test_utils/ov_tensor_utils.hpp" #include "common_test_utils/file_utils.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" #include "openvino/op/parameter.hpp" #include "openvino/op/constant.hpp" -#include "openvino/op/reduce_max.hpp" -#include "openvino/op/reduce_mean.hpp" -#include "openvino/op/reduce_min.hpp" -#include "openvino/op/reduce_prod.hpp" #include "openvino/op/reduce_sum.hpp" #include "openvino/op/add.hpp" #include "openvino/op/multiply.hpp" #include "openvino/runtime/intel_gpu/properties.hpp" -#include "shared_test_classes/base/ov_subgraph.hpp" namespace { @@ -106,58 +100,4 @@ INSTANTIATE_TEST_SUITE_P( ::testing::Values(false)), ReduceSumSqueezeTest::getTestCaseName); -using ReduceParams = std::tuple, // Reduce axes - bool>; // Keep dims - -template -class ReduceTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { -public: - static std::string getTestCaseName(const testing::TestParamInfo& obj) { - const auto& [input_shape, precision, axes, keep_dims] = obj.param; - std::ostringstream result; - result << "IS=" << ov::test::utils::vec2str(input_shape) << "_"; - result << "axes=" << ov::test::utils::vec2str(axes) << "_"; - result << "keep_dims=" << keep_dims << "_"; - result << "precision=" << precision; - return result.str(); - } - -protected: - void SetUp() override { - targetDevice = ov::test::utils::DEVICE_GPU; - const auto& [input_shape, precision, axes, keep_dims] = GetParam(); - auto input = std::make_shared(precision, input_shape); - auto axes_node = ov::op::v0::Constant::create(ov::element::i64, {axes.size()}, axes); - auto reduce = std::make_shared(input, axes_node, keep_dims); - auto result = std::make_shared(reduce); - function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input}, "Reduce"); - } -}; - -using ReduceMeanTest = ReduceTest; -using ReduceMaxTest = ReduceTest; -using ReduceMinTest = ReduceTest; -using ReduceProdTest = ReduceTest; -using ReduceSumTest = ReduceTest; - -TEST_P(ReduceMeanTest, Inference) { run(); } -TEST_P(ReduceMaxTest, Inference) { run(); } -TEST_P(ReduceMinTest, Inference) { run(); } -TEST_P(ReduceProdTest, Inference) { run(); } -TEST_P(ReduceSumTest, Inference) { run(); } - -const auto reduce_test_params = ::testing::Combine(::testing::Values(ov::Shape{1, 24, 1024, 64}), - ::testing::Values(ov::element::f32), - ::testing::Values(std::vector{3}), - ::testing::Values(true)); -// ::testing::Values(true, false)); - -INSTANTIATE_TEST_SUITE_P(smoke_ReduceMeanTest, ReduceMeanTest, reduce_test_params, ReduceMeanTest::getTestCaseName); -INSTANTIATE_TEST_SUITE_P(smoke_ReduceMaxTest, ReduceMaxTest, reduce_test_params, ReduceMaxTest::getTestCaseName); -INSTANTIATE_TEST_SUITE_P(smoke_ReduceMinTest, ReduceMinTest, reduce_test_params, ReduceMinTest::getTestCaseName); -INSTANTIATE_TEST_SUITE_P(smoke_ReduceProdTest, ReduceProdTest, reduce_test_params, ReduceProdTest::getTestCaseName); -INSTANTIATE_TEST_SUITE_P(smoke_ReduceSumTest, ReduceSumTest, reduce_test_params, ReduceSumTest::getTestCaseName); - -} // namespace +} // namespace From 7f9c5116a63b1755f6fef1a87de1478ce04f32fa Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Wed, 13 May 2026 16:23:12 +0200 Subject: [PATCH 068/121] Implemented converter and tests for the Reshape operation (#127) --- .../mlir/common/converters/reshape.hpp | 65 +++++++++++++++++++ .../mlir/conversion/patterns.cpp | 5 ++ .../mlir/conversion/patterns.hpp | 6 ++ .../src/transformations/mlir/convert.cpp | 1 + .../functional/mlir_op/binary_eltwise.cpp | 12 ++-- .../tests/functional/mlir_op/transpose.cpp | 47 ++++++++++++-- .../functional/mlir_op/unary_eltwise.cpp | 2 +- 7 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 src/common/transformations/src/transformations/mlir/common/converters/reshape.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/reshape.hpp b/src/common/transformations/src/transformations/mlir/common/converters/reshape.hpp new file mode 100644 index 00000000000000..5ff856f34be20a --- /dev/null +++ b/src/common/transformations/src/transformations/mlir/common/converters/reshape.hpp @@ -0,0 +1,65 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "../convert_common.hpp" +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "openvino/pass/pattern/op/wrap_type.hpp" + +namespace ov { +namespace mlir { + +struct ConvertReshape { + Operation* operator()(ConversionContext& context, NodePtr node) { + const auto in_shape = node->get_input_partial_shape(0); + const auto out_shape = node->get_output_partial_shape(0); + assert(in_shape.rank().is_static() && out_shape.rank().is_static()); + assert(llvm::all_of(in_shape, std::mem_fn(&ov::Dimension::is_static))); + assert(llvm::all_of(out_shape, std::mem_fn(&ov::Dimension::is_static))); + const auto in_rank = static_cast(in_shape.rank().get_length()); + const auto out_rank = static_cast(out_shape.rank().get_length()); + const bool expand = out_rank >= in_rank; + + // Build reassociation by matching accumulated products of src/dst dims. + // Each group maps one src dim to multiple dst dims (expand) or vice versa (collapse). + SmallVector reassociation; + for (size_t src_i = 0, dst_i = 0; src_i < in_rank && dst_i < out_rank; src_i++, dst_i++) { + ReassociationIndices group; + int64_t src_prod = in_shape[src_i].get_length(); + int64_t dst_prod = out_shape[dst_i].get_length(); + if (expand) { + // one src dim -> multiple dst dims + group.push_back(dst_i); + while (src_prod != dst_prod && dst_i < out_rank) { + dst_prod *= out_shape[++dst_i].get_length(); + group.push_back(dst_i); + } + } else { + // multiple src dims -> one dst dim + group.push_back(src_i); + while (src_prod != dst_prod && src_i < in_rank) { + src_prod *= in_shape[++src_i].get_length(); + group.push_back(src_i); + } + } + assert(src_prod == dst_prod && "shape mismatch: incompatible reshape"); + reassociation.push_back(group); + } + + auto dst_shape = llvm::to_vector(llvm::map_range(out_shape, std::mem_fn(&ov::Dimension::get_length))); + auto dst_type = + RankedTensorType::get(dst_shape, importPrecision(context.context, node->get_output_element_type(0))); + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto input = context.getInputs(node)[0]; + return expand ? tensor::ExpandShapeOp::create(builder, loc, dst_type, input, reassociation) + : tensor::CollapseShapeOp::create(builder, loc, dst_type, input, reassociation); + } +}; + +} // namespace mlir +} // namespace ov diff --git a/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp b/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp index 429fc28f8e5e30..a663a25d5de6ca 100644 --- a/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp +++ b/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include "../common/converters/gather.hpp" #include "../common/converters/matmul.hpp" #include "../common/converters/reduce.hpp" +#include "../common/converters/reshape.hpp" #include "../common/converters/sdpa.hpp" #include "../common/converters/shape_of.hpp" #include "../common/converters/slice.hpp" @@ -89,6 +91,9 @@ template class ReducePattern; template class ReducePattern; template class ReducePattern; +ReshapePattern::ReshapePattern() + : MarkPattern(wrap_type({any_input(), any_input()}), ConvertReshape()) {} + SDPAPattern::SDPAPattern() : MarkPattern(wrap_type(), ConvertSDPA()) {} diff --git a/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp b/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp index a7db53429253e3..2f0d70f9769f5f 100644 --- a/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp +++ b/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp @@ -50,6 +50,12 @@ class ReducePattern : public MarkPattern { ReducePattern(); }; +class ReshapePattern : public MarkPattern { +public: + OPENVINO_RTTI("ReshapePattern", "0"); + ReshapePattern(); +}; + class SDPAPattern : public MarkPattern { public: OPENVINO_RTTI("SDPAPattern", "0"); diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index bc6d272b405b12..df80ff6ce6c296 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -383,6 +383,7 @@ void injectMLIR(std::shared_ptr model, manager.register_pass>(); manager.register_pass>(); manager.register_pass>(); + manager.register_pass(); manager.register_pass(); manager.register_pass(); manager.register_pass(); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp index c5d5bd48a4345b..556a00c4e88a01 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp @@ -91,9 +91,9 @@ const auto same_shape_params = const auto broadcast_params = ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 1536}), ::testing::Values(ov::Shape{1536}), ::testing::Values(ov::element::f16)); -#define INSTANTIATE_TS(Name) \ - INSTANTIATE_TEST_SUITE_P(smoke_BinaryElementwise##Name##_same_shape, Name, same_shape_params, Name::getTestCaseName); \ - INSTANTIATE_TEST_SUITE_P(smoke_BinaryElementwise##Name##_broadcast, Name, broadcast_params, Name::getTestCaseName) +#define INSTANTIATE_TS(Name) \ + INSTANTIATE_TEST_SUITE_P(mlir_BinaryElementwise##Name##_same_shape, Name, same_shape_params, Name::getTestCaseName); \ + INSTANTIATE_TEST_SUITE_P(mlir_BinaryElementwise##Name##_broadcast, Name, broadcast_params, Name::getTestCaseName) INSTANTIATE_TS(AddTest); INSTANTIATE_TS(SubtractTest); @@ -162,9 +162,9 @@ const auto const_broadcast_params = ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 1536}), ::testing::Values(ov::Shape{1536}), ::testing::Values(ov::element::f16)); #undef INSTANTIATE_TS -#define INSTANTIATE_TS(Name) \ - INSTANTIATE_TEST_SUITE_P(smoke_BinaryElementwise##Name##_scalar, Name, const_scalar_params, Name::getTestCaseName); \ - INSTANTIATE_TEST_SUITE_P(smoke_BinaryElementwise##Name##_broadcast, Name, const_broadcast_params, Name::getTestCaseName) +#define INSTANTIATE_TS(Name) \ + INSTANTIATE_TEST_SUITE_P(mlir_BinaryElementwise##Name##_scalar, Name, const_scalar_params, Name::getTestCaseName); \ + INSTANTIATE_TEST_SUITE_P(mlir_BinaryElementwise##Name##_broadcast, Name, const_broadcast_params, Name::getTestCaseName) INSTANTIATE_TS(AddConstTest); INSTANTIATE_TS(SubtractConstTest); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp index 157c1850c88ce0..31866a2468492e 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp @@ -7,6 +7,7 @@ #include "common_test_utils/ov_tensor_utils.hpp" #include "openvino/op/constant.hpp" #include "openvino/op/parameter.hpp" +#include "openvino/op/reshape.hpp" #include "openvino/op/result.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" @@ -42,10 +43,48 @@ TEST_P(TransposeTest, Inference) { run(); } -const auto test_params = ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 24, 64}, ov::Shape{1, 128, 24, 64}), - ::testing::Values(std::vector{0, 2, 1, 3}), - ::testing::Values(ov::element::f16)); +const auto transpose_params = ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 24, 64}, ov::Shape{1, 128, 24, 64}), + ::testing::Values(std::vector{0, 2, 1, 3}), + ::testing::Values(ov::element::f16)); +INSTANTIATE_TEST_SUITE_P(mlir_Transpose, TransposeTest, transpose_params, TransposeTest::getTestCaseName); -INSTANTIATE_TEST_SUITE_P(smoke_Transpose, TransposeTest, test_params, TransposeTest::getTestCaseName); +// Params: input shape, output shape, order, precision +using ReshapeAndTransposeParams = std::tuple, ov::element::Type>; + +class ReshapeAndTransposeTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [input_shape, output_shape, order, precision] = obj.param; + std::ostringstream result; + result << "Input=" << ov::test::utils::vec2str(input_shape) << "_"; + result << "Output=" << ov::test::utils::vec2str(output_shape) << "_"; + result << "Order=" << ov::test::utils::vec2str(order) << "_"; + result << "precision=" << precision; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto& [input_shape, output_shape, order, precision] = GetParam(); + auto input = std::make_shared(precision, input_shape); + std::vector shape_data(output_shape.begin(), output_shape.end()); + auto shape_const = ov::op::v0::Constant::create(ov::element::i64, {output_shape.size()}, shape_data); + auto reshape = std::make_shared(input, shape_const, false); + auto order_const = ov::op::v0::Constant::create(ov::element::i64, {order.size()}, order); + auto op = std::make_shared(reshape, order_const); + auto result = std::make_shared(op); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input}); + } +}; + +TEST_P(ReshapeAndTransposeTest, Inference) { + run(); +} + +const auto reshape_and_transpose_params = + ::testing::Values(ReshapeAndTransposeParams{ov::Shape{1, 1024, 1536}, ov::Shape{1, 1024, 24, 64}, std::vector{0, 2, 1, 3}, ov::element::f16}, + ReshapeAndTransposeParams{ov::Shape{1, 128, 1536}, ov::Shape{1, 128, 24, 64}, std::vector{0, 2, 1, 3}, ov::element::f16}); +INSTANTIATE_TEST_SUITE_P(mlir_ReshapeAndTranspose, ReshapeAndTransposeTest, reshape_and_transpose_params, ReshapeAndTransposeTest::getTestCaseName); } // namespace diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp index f7c82c86904d7b..90eeb9ccb94a74 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp @@ -69,7 +69,7 @@ DEFINE_TEST(TanhTest) const auto test_params = ::testing::Combine(::testing::Values(ov::Shape{1, 24, 1024, 1}, ov::Shape{1, 24, 128, 1}), ::testing::Values(ov::element::f16)); -#define INSTANTIATE_TS(Name) INSTANTIATE_TEST_SUITE_P(smoke_UnaryElementwise##Name, Name, test_params, Name::getTestCaseName) +#define INSTANTIATE_TS(Name) INSTANTIATE_TEST_SUITE_P(mlir_UnaryElementwise##Name, Name, test_params, Name::getTestCaseName) INSTANTIATE_TS(AbsTest); INSTANTIATE_TS(CeilingTest); From 747c5df674fd47db78ad16f9a47c45fcbaa7683a Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Wed, 20 May 2026 18:03:49 +0200 Subject: [PATCH 069/121] Adapt OV to new llvm (#143) Signed-off-by: dchigarev --- .../transformations/src/transformations/mlir/mlir_op.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index a22f04b2cc2d85..2373d023a3e7a9 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -190,7 +190,8 @@ std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext // Specify target machine if (!triple.empty() && !cpuName.empty()) { std::string error; - const llvm::Target* target = llvm::TargetRegistry::lookupTarget(triple, error); + llvm::Triple tripleObj(triple); + const llvm::Target* target = llvm::TargetRegistry::lookupTarget(tripleObj, error); if (!target) { llvm::errs() << "Error while looking up target triple: "; llvm::errs() << error << "\n"; From d15d7b98ee40427ab32e4f18984dab0039d9cf85 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Thu, 28 May 2026 21:45:52 +0200 Subject: [PATCH 070/121] Added ConcatTest and TransposeConcatTest (#133) --- .../tests/functional/mlir_op/concat.cpp | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp new file mode 100644 index 00000000000000..705264dc1d5cee --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp @@ -0,0 +1,82 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/op/concat.hpp" +#include "openvino/op/transpose.hpp" + +#include "common_test_utils/ov_tensor_utils.hpp" +#include "openvino/op/parameter.hpp" +#include "openvino/op/result.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" + +namespace { + +// Params: precision +using ConcatParams = ov::element::Type; + +class ConcatTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + std::ostringstream result; + result << "precision=" << obj.param; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto precision = GetParam(); + + auto input0 = std::make_shared(precision, ov::Shape{1, 24, 1024, 64}); + auto input1 = std::make_shared(precision, ov::Shape{1, 24, 128, 64}); + auto concat = std::make_shared(ov::OutputVector{input0, input1}, 2); + + auto result = std::make_shared(concat); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input0, input1}); + } +}; + +TEST_P(ConcatTest, Inference) { + run(); +} + +INSTANTIATE_TEST_SUITE_P(mlir_Concat, ConcatTest, ::testing::Values(ov::element::f16), ConcatTest::getTestCaseName); + +// Params: precision +using TransposeConcatParams = ov::element::Type; + +class TransposeConcatTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + std::ostringstream result; + result << "precision=" << obj.param; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto precision = GetParam(); + const std::vector order{0, 2, 1, 3}; + auto order_const = ov::op::v0::Constant::create(ov::element::i64, {order.size()}, order); + + auto input0 = std::make_shared(precision, ov::Shape{1, 1024, 24, 64}); + auto transpose0 = std::make_shared(input0, order_const); + + auto input1 = std::make_shared(precision, ov::Shape{1, 128, 24, 64}); + auto transpose1 = std::make_shared(input1, order_const); + + auto concat = std::make_shared(ov::OutputVector{transpose0, transpose1}, 2); + auto result = std::make_shared(concat); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input0, input1}); + } +}; + +TEST_P(TransposeConcatTest, Inference) { + run(); +} + +INSTANTIATE_TEST_SUITE_P(mlir_TransposeConcat, TransposeConcatTest, ::testing::Values(ov::element::f16), TransposeConcatTest::getTestCaseName); + +} // namespace From e94b479038ee788393d1a086db9d6adf6b9d0942 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Wed, 10 Jun 2026 15:01:52 +0200 Subject: [PATCH 071/121] [MLIR Convertors] Added support for batch matmul (#150) * [MLIR Convertors] Added support for batch matmul If the leading dims are 1, collapse to 2 dims and convert to Matmul*Op. Expand shapes of inputs to make them equal to output If the inputs of MatMul have > 2 dims, convert to BatchMatmul*Op. --- .../mlir/common/converters/matmul.hpp | 71 +++++++++++++++++-- .../mlir/conversion/patterns.cpp | 5 +- .../tests/functional/mlir_op/matmul.cpp | 56 +++++++++++++++ 3 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/matmul.hpp b/src/common/transformations/src/transformations/mlir/common/converters/matmul.hpp index 54410e6f577ce4..e00b40acef2810 100644 --- a/src/common/transformations/src/transformations/mlir/common/converters/matmul.hpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/matmul.hpp @@ -39,15 +39,74 @@ struct ConvertMatMul { bool isTransposedB = matmul_node->get_transpose_b(); assert(!(isTransposedA && isTransposedB)); + // TODO: move the unit-dimension-folding logic to the graph-compiler + bool batch = true; + auto canCollapse = [](mlir::Value tensor) { // rank <= 2 or all leading dimensions are 1 + auto shape = mlir::cast(tensor.getType()).getShape().drop_back(2); + return std::all_of(shape.begin(), shape.end(), [](int64_t d) { + return d == 1; + }); + }; + if (canCollapse(ins[0]) && canCollapse(ins[1]) && canCollapse(outs[0])) { + auto collapse = [&](Value tensor) -> Value { // Has no-op if rank <= 2 + auto shape = mlir::cast(tensor.getType()).getShape(); + int64_t rank = shape.size(); + if (rank <= 2) + return tensor; + SmallVector reassoc; + ReassociationIndices leading; + for (int64_t i = 0; i < rank - 1; ++i) + leading.push_back(i); + reassoc.push_back(leading); + reassoc.push_back(ReassociationIndices{rank - 1}); + return tensor::CollapseShapeOp::create(builder, loc, tensor, reassoc).getResult(); + }; + ins[0] = collapse(ins[0]); + ins[1] = collapse(ins[1]); + outs[0] = collapse(outs[0]); + batch = false; + } + + auto expand = [&](Value tensor) -> Value { + int64_t rank = ov_output_shape.size(); + auto type = mlir::cast(tensor.getType()); + auto shape = type.getShape(); + if (shape.size() == rank) + return tensor; + SmallVector reassoc; + ReassociationIndices leading; + for (int64_t i = 0; i < rank - 1; ++i) + leading.push_back(i); + reassoc.push_back(leading); + reassoc.push_back(ReassociationIndices{rank - 1}); + SmallVector new_shape(rank, 1); + std::copy(shape.begin(), shape.end(), new_shape.end() - shape.size()); + auto new_type = mlir::RankedTensorType::get(new_shape, type.getElementType()); + return tensor::ExpandShapeOp::create(builder, loc, new_type, tensor, reassoc).getResult(); + }; + Operation* matmul; - if (isTransposedA) { - matmul = linalg::MatmulTransposeAOp::create(builder, loc, ins, outs); - } else if (isTransposedB) { - matmul = linalg::MatmulTransposeBOp::create(builder, loc, ins, outs); + if (batch) { + // Expand if required, to have all inputs of the same rank. + ins[0] = expand(ins[0]); + ins[1] = expand(ins[1]); + if (isTransposedA) { + matmul = linalg::BatchMatmulTransposeAOp::create(builder, loc, ins, outs); + } else if (isTransposedB) { + matmul = linalg::BatchMatmulTransposeBOp::create(builder, loc, ins, outs); + } else { + matmul = linalg::BatchMatmulOp::create(builder, loc, ins, outs); + } } else { - matmul = linalg::MatmulOp::create(builder, loc, ins, outs); + if (isTransposedA) { + matmul = linalg::MatmulTransposeAOp::create(builder, loc, ins, outs); + } else if (isTransposedB) { + matmul = linalg::MatmulTransposeBOp::create(builder, loc, ins, outs); + } else { + matmul = linalg::MatmulOp::create(builder, loc, ins, outs); + } + matmul = expand(matmul->getResult(0)).getDefiningOp(); } - return matmul; } }; diff --git a/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp b/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp index a663a25d5de6ca..47dcb3984df798 100644 --- a/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp +++ b/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp @@ -73,10 +73,7 @@ MatMulPattern::MatMulPattern() wrap_type({any_input(), any_input()}, [](const Output& output) { auto node = std::dynamic_pointer_cast(output.get_node_shared_ptr()); assert(node); - // FIXME: current code limitation - return !has_dynamic_rank(node) && !(node->get_transpose_a() && node->get_transpose_b()) && - node->get_input_partial_shape(0).rank().get_length() == 2 && - node->get_input_partial_shape(1).rank().get_length() == 2; + return !has_dynamic_rank(node) && !(node->get_transpose_a() && node->get_transpose_b()); }), ConvertMatMul()) {} diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp new file mode 100644 index 00000000000000..04610dcca14cab --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/op/matmul.hpp" + +#include "openvino/op/parameter.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" + +namespace { + +using BatchMatMulParams = std::tuple; + +class BatchMatMulTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [a_shape, b_shape, tr_a, tr_b, prec] = obj.param; + std::ostringstream result; + result << "A=" << ov::test::utils::vec2str(a_shape) << "_"; + result << "B=" << ov::test::utils::vec2str(b_shape) << "_"; + result << "trA=" << tr_a << "_trB=" << tr_b << "_"; + result << "precision=" << prec; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto& [a_shape, b_shape, tr_a, tr_b, prec] = GetParam(); + auto param_a = std::make_shared(prec, a_shape); + auto param_b = std::make_shared(prec, b_shape); + auto matmul = std::make_shared(param_a, param_b, tr_a, tr_b); + auto result = std::make_shared(matmul); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{param_a, param_b}, "BatchMatMul"); + abs_threshold = 100.f; + } +}; + +TEST_P(BatchMatMulTest, Inference) { + run(); +} + +INSTANTIATE_TEST_SUITE_P(mlir_BatchMatMul, + BatchMatMulTest, + ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 1536}), + ::testing::Values(ov::Shape{1536, 1536}), + ::testing::Values(false), + ::testing::Values(true), + ::testing::Values(ov::element::f16)), + BatchMatMulTest::getTestCaseName); + +} // namespace From 8cd1c890eac300d5f14648c2f4f7990301271693 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Thu, 18 Jun 2026 18:16:15 +0000 Subject: [PATCH 072/121] Use empty tensor as the div output --- .../src/transformations/mlir/common/converters/reduce.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/transformations/src/transformations/mlir/common/converters/reduce.hpp b/src/common/transformations/src/transformations/mlir/common/converters/reduce.hpp index 49c7db91cdd41f..a4b14de939e088 100644 --- a/src/common/transformations/src/transformations/mlir/common/converters/reduce.hpp +++ b/src/common/transformations/src/transformations/mlir/common/converters/reduce.hpp @@ -92,7 +92,8 @@ struct ConvertReduce { auto divisor = ::mlir::arith::ConstantOp::create(builder, loc, ::mlir::DenseElementsAttr::get(result_type, divisor_attr)); - result = ::mlir::linalg::DivOp::create(builder, loc, ValueRange{result, divisor}, ValueRange{result}) + auto empty = ::mlir::tensor::EmptyOp::create(builder, loc, result_type, ValueRange{}); + result = ::mlir::linalg::DivOp::create(builder, loc, ValueRange{result, divisor}, ValueRange{empty}) .getResult(0); } From 050230c6f4c4f7594b81fd1688517d3b8c07fedf Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Mon, 29 Jun 2026 15:00:43 +0200 Subject: [PATCH 073/121] Match matmul-rms-norm-concat subgraph (#151) --- .../mlir/conversion/patterns.cpp | 2 + .../src/transformations/mlir/convert.cpp | 18 +- .../transformations/mlir/graph_converter.cpp | 16 +- .../transformations/mlir/graph_converter.hpp | 18 +- .../transformations/mlir/subgraph_tracker.cpp | 18 +- .../transformations/mlir/subgraph_tracker.hpp | 2 + .../src/plugin/transformations_pipeline.cpp | 1 + .../mlir_op/matmul_rms_norm_concat.cpp | 181 ++++++++++++++++++ .../functional/single_layer_tests/reduce.cpp | 5 +- 9 files changed, 243 insertions(+), 18 deletions(-) create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp diff --git a/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp b/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp index 47dcb3984df798..26ceb7b6beacdd 100644 --- a/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp +++ b/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -138,6 +139,7 @@ template class BinaryEltwisePattern; template class BinaryEltwisePattern; template class BinaryEltwisePattern; template class BinaryEltwisePattern; +template class BinaryEltwisePattern; template UnaryEltwisePattern::UnaryEltwisePattern() diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index df80ff6ce6c296..c772869e49676b 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -152,7 +153,8 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, const ov::OutputVector& inputs, const ov::NodeVector& nodes, const ov::OutputVector& outputs, - SmallVector& keptInputIndices) { + SmallVector& keptInputIndices, + const std::string& function_name = "entry") { // Split inputs: splat constants are inlined, runtime inputs become function args. ov::OutputVector runtime_inputs; SmallVector is_constant(inputs.size(), false); @@ -177,7 +179,7 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, auto memref_args = inputTypes; memref_args.append(outputTypes); const auto funcType = mlir::FunctionType::get(context, ArrayRef(memref_args), ArrayRef(SmallVector())); - auto func = moduleBuilder.create(funcLoc, "entry", funcType); + auto func = moduleBuilder.create(funcLoc, function_name, funcType); auto block_builder = mlir::OpBuilder::atBlockBegin(func.addEntryBlock() /* TODO: Add logger here */); // Affix target information attribute to the module to be used, at its discretion, @@ -268,7 +270,8 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, std::shared_ptr loweringContext) { SmallVector keptInputIndices; mlir::OwningOpRef module = - ngraph_to_mlir(context, subgraph->inputs, subgraph->nodes, subgraph->outputs, keptInputIndices); + ngraph_to_mlir(context, subgraph->inputs, subgraph->nodes, subgraph->outputs, keptInputIndices, + subgraph->function_name); ov::OutputVector inputs; inputs.reserve(keptInputIndices.size()); @@ -357,7 +360,13 @@ class Partitioner : public ov::pass::ModelPass { } ); for(auto node: model->get_ordered_ops()) { - tracker.add_node(node, get_subgraph_mark(node)); + if (auto name = get_subgraph_mark(node); !name.empty()) { + tracker.add_node(node, true); + if (auto subgraph = tracker.get_current_subgraph(node)) + subgraph->function_name = name; + } else { + tracker.add_node(node, false); + } } tracker.finalize(); return true; @@ -378,6 +387,7 @@ void injectMLIR(std::shared_ptr model, manager.register_pass>(); manager.register_pass>(); manager.register_pass>(); + manager.register_pass>(); manager.register_pass>(); manager.register_pass>(); manager.register_pass>(); diff --git a/src/common/transformations/src/transformations/mlir/graph_converter.cpp b/src/common/transformations/src/transformations/mlir/graph_converter.cpp index bbf923d9d24b31..94d379ac8c9cdc 100644 --- a/src/common/transformations/src/transformations/mlir/graph_converter.cpp +++ b/src/common/transformations/src/transformations/mlir/graph_converter.cpp @@ -75,24 +75,32 @@ const std::string& subgraph_mark() { return mark; } -void set_subgraph_mark(NodePtr node) { - node->get_rt_info()[subgraph_mark()]; +void set_subgraph_mark(NodePtr node, const std::string& name) { + node->get_rt_info()[subgraph_mark()] = name; } -bool get_subgraph_mark(NodePtr node) { +bool has_subgraph_mark(NodePtr node) { return node->get_rt_info().count(subgraph_mark()); } +std::string get_subgraph_mark(NodePtr node) { + const auto& rti = node->get_rt_info(); + auto it = rti.find(subgraph_mark()); + return it == rti.end() ? "" : it->second.as(); +} MarkPattern::MarkPattern(NodePtr pattern, GraphConverter::Convertor convertor) { auto callback = [convertor](ov::pass::pattern::Matcher& m) { - // TODO: support multi-node patterns marking auto node = m.get_match_root(); set_subgraph_mark(node); GraphConverter::set_convertor(node, convertor); return true; }; + auto m = std::make_shared(pattern, "MarkPattern"); + register_matcher(m, callback); +} +MarkPattern::MarkPattern(NodePtr pattern, MarkPattern::Callback callback) { auto m = std::make_shared(pattern, "MarkPattern"); register_matcher(m, callback); } diff --git a/src/common/transformations/src/transformations/mlir/graph_converter.hpp b/src/common/transformations/src/transformations/mlir/graph_converter.hpp index 95f6b1524fc7a8..6550870a9dc53b 100644 --- a/src/common/transformations/src/transformations/mlir/graph_converter.hpp +++ b/src/common/transformations/src/transformations/mlir/graph_converter.hpp @@ -17,7 +17,6 @@ namespace ov { namespace mlir { -using ::mlir::Value; using ::mlir::MLIRContext; using ::mlir::OpBuilder; using ::mlir::Operation; @@ -25,7 +24,7 @@ using ::mlir::SmallVector; using ::mlir::ValueRange; class GraphConverter { - static std::string rt_info_convertor (); + static std::string rt_info_convertor(); ConversionContext _ctx; public: @@ -51,15 +50,22 @@ class GraphConverter { const std::string& subgraph_mark(); -void set_subgraph_mark(NodePtr node); +// name is the function name, that will contain the subgraph after conversion. The nodes are grouped by this name and +// could be splitted into multiple subgraphs by marking with different names. +void set_subgraph_mark(NodePtr node, const std::string& name = "entry"); -bool get_subgraph_mark(NodePtr node); +bool has_subgraph_mark(NodePtr node); + +// Returns "" if node is not marked +std::string get_subgraph_mark(NodePtr node); class MarkPattern : public ov::pass::MatcherPass { public: OPENVINO_RTTI("MarkPattern", "0"); MarkPattern(NodePtr pattern, GraphConverter::Convertor convertor); + using Callback = std::function; + MarkPattern(NodePtr pattern, Callback callback); }; -} // namespace mlir -} // namespace ov \ No newline at end of file +} // namespace mlir +} // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp b/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp index 6eb56846145510..d3311dd65ce0a6 100644 --- a/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp +++ b/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp @@ -36,9 +36,16 @@ void SubgraphTracker::add_node (NodePtr node, bool belongs) { } if(belongs) { - // Below we refuse to merge subgraphs if _all_ of them cannot merge to a single subgraph, this is rough because - // there are cases when a _part_ of the input subgraphs can be merged together and consume the new node and other (conflicting) subgraphs will come as inputs -- TODO. - // TODO: leave only those input subgraphs that are not conflicting with other subgraphs nor with any dependencies + // Remove input_subgraphs from input_dependencies: a subgraph depending on itself + // is not a cycle — it just means multiple paths converge inside the same subgraph. + for(auto id: input_subgraphs) + input_dependencies.erase(ov::symbol::ancestor_of(id)); + + // Below we refuse to merge subgraphs if all of them cannot merge to a single subgraph, this is rough because + // there are cases when a part of the input subgraphs can be merged together and consume the new node and other + // (conflicting) subgraphs will come as inputs + // TODO: leave only those input subgraphs that are not conflicting with other subgraphs nor with any + // dependencies if(input_subgraphs.empty() || intersected(input_subgraphs, input_dependencies)) { // no input subgraphs || cannot merge all due to cycles try_terminate_subgraphs(input_subgraphs, node); @@ -69,6 +76,11 @@ void SubgraphTracker::add_node (NodePtr node, bool belongs) { set_dependencies(node, input_dependencies); } +SubgraphPtr SubgraphTracker::get_current_subgraph(NodePtr node) { + auto id = get_subgraph_id(node); + return id ? get_subgraph(id) : nullptr; +} + void SubgraphTracker::finalize() { for(auto subgraph_record: m_subgraphs) { terminate_subgraph(subgraph_record.first); diff --git a/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp b/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp index b43130ded11048..07411d1b575a74 100644 --- a/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp +++ b/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp @@ -17,6 +17,7 @@ struct Subgraph { ov::OutputVector inputs; ov::OutputVector outputs; std::vector output_consumers; + std::string function_name; // Consumes other subgraph void merge (Subgraph& other); @@ -36,6 +37,7 @@ class SubgraphTracker { SubgraphTracker(Finalizer finalizer); void add_node (NodePtr node, bool belongs); void finalize(); + SubgraphPtr get_current_subgraph(NodePtr node); private: diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index 1b857a8c4c0c8f..9426c9ae3cfad1 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -595,6 +595,7 @@ void TransformationsPipeline::apply(std::shared_ptr func) { if (ov::pass::is_mlir_transform_enabled()) { pass_config->disable(); pass_config->disable(); + pass_config->disable(); } manager.register_pass(); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp new file mode 100644 index 00000000000000..1e0f3444851f7c --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp @@ -0,0 +1,181 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/op/add.hpp" +#include "openvino/op/concat.hpp" +#include "openvino/op/constant.hpp" +#include "openvino/op/divide.hpp" +#include "openvino/op/matmul.hpp" +#include "openvino/op/multiply.hpp" +#include "openvino/op/parameter.hpp" +#include "openvino/op/power.hpp" +#include "openvino/op/reduce_mean.hpp" +#include "openvino/op/reshape.hpp" +#include "openvino/op/result.hpp" +#include "openvino/op/sqrt.hpp" +#include "openvino/op/transpose.hpp" +#include "shared_test_classes/base/benchmark.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" + +namespace { + +// A(1xSEQx1536xf16) +// ▼ +// MatMul(transpose B) → Add(const) → Reshape(1xSEQx24x64) → Transpose(1x24xSEQx64) +// ▲ ▼ ▼ +// B(1536x1536xf16) Power(B=const) │ +// ▼ │ +// ReduceMean(axis=3) │ +// ▼ │ +// Add(B=const) │ +// ▼ │ +// Sqrt │ +// ▼ │ +// Divide(B=const) │ +// ▼ ▼ +// Multiply(Divide × Transpose) +// ▼ +// Multiply(× const) + +// Builds the MatMul+RmsNorm subgraph for a given A shape and shared B parameter. +// Returns the output node (Multiply × scale). +static std::shared_ptr build_matmul_rmsnorm(ov::element::Type prec, + const std::shared_ptr& param_a, + const std::shared_ptr& param_b) { + const auto& a_shape = param_a->get_shape(); + const int64_t hidden = static_cast(a_shape.back()); + const int64_t seq = static_cast(a_shape[1]); + const int64_t heads = 24; + const int64_t head_size = hidden / heads; + + auto matmul = std::make_shared(param_a, param_b, false, true); + + auto bias = ov::op::v0::Constant::create(prec, {(size_t)hidden}, std::vector(hidden, 0.1f)); + auto add1 = std::make_shared(matmul, bias); + + auto shape_val = ov::op::v0::Constant::create(ov::element::i64, {4}, std::vector{1, seq, heads, head_size}); + auto reshape = std::make_shared(add1, shape_val, false); + + auto order = ov::op::v0::Constant::create(ov::element::i64, {4}, std::vector{0, 2, 1, 3}); + auto transpose = std::make_shared(reshape, order); + + auto exp2 = ov::op::v0::Constant::create(prec, {1}, std::vector{2.f}); + auto power = std::make_shared(transpose, exp2); + + auto axes = ov::op::v0::Constant::create(ov::element::i64, {1}, std::vector{3}); + auto reduce_mean = std::make_shared(power, axes, true); + + auto eps = ov::op::v0::Constant::create(prec, {1}, std::vector{1e-5f}); + auto add2 = std::make_shared(reduce_mean, eps); + + auto sqrt_node = std::make_shared(add2); + + auto one = ov::op::v0::Constant::create(prec, {1}, std::vector{1.f}); + auto divide = std::make_shared(one, sqrt_node); + + auto mul1 = std::make_shared(divide, transpose); + + auto scale = ov::op::v0::Constant::create(prec, {1}, std::vector{2.f}); + return std::make_shared(mul1, scale); +} + +// ── MatMulRmsnormTest ───────────────────────────────────────────────────────── + +using MatMulRmsnormParams = std::tuple; // B shape + +class MatMulRmsnormTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [a_shape, b_shape] = obj.param; + std::ostringstream result; + result << "A=" << ov::test::utils::vec2str(a_shape) << "_"; + result << "B=" << ov::test::utils::vec2str(b_shape); + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto prec = ov::element::f16; + const auto& [a_shape, b_shape] = GetParam(); + + auto param_a = std::make_shared(prec, a_shape); + auto param_b = std::make_shared(prec, b_shape); + auto out = build_matmul_rmsnorm(prec, param_a, param_b); + auto result = std::make_shared(out); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{param_a, param_b}, "MatMulRmsnorm"); + } +}; + +class MatMulRmsnormBenchmark : public ov::test::BenchmarkLayerTest {}; + +TEST_P(MatMulRmsnormTest, Inference) { + run(); +} +TEST_P(MatMulRmsnormBenchmark, Inference) { + run_benchmark("MLIROp"); +} + +const auto rmsnormParams = ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 1536}), ::testing::Values(ov::Shape{1536, 1536})); +INSTANTIATE_TEST_SUITE_P(mlir_MatMulRmsnormTest, MatMulRmsnormTest, rmsnormParams, MatMulRmsnormTest::getTestCaseName); +INSTANTIATE_TEST_SUITE_P(bench_MatMulRmsnormBenchmark, MatMulRmsnormBenchmark, rmsnormParams, MatMulRmsnormTest::getTestCaseName); + +// ── MatMulRmsnormConcatTest ─────────────────────────────────────────────────── +// +// Runs two MatMul+RmsNorm graphs (different A sequence lengths) sharing B, +// then concatenates their outputs along the sequence axis (axis=2). +// +// branch0: A0(1xSEQ0x1536) ─┐ +// branch1: A1(1xSEQ1x1536) ─┤ → Concat(axis=2) +// └─ shared B(1536x1536) + +using MatMulRmsnormConcatParams = std::tuple; // B shape + +class MatMulRmsnormConcatTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [a0_shape, a1_shape, b_shape] = obj.param; + std::ostringstream result; + result << "A0=" << ov::test::utils::vec2str(a0_shape) << "_"; + result << "A1=" << ov::test::utils::vec2str(a1_shape) << "_"; + result << "B=" << ov::test::utils::vec2str(b_shape); + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto prec = ov::element::f16; + const auto& [a0_shape, a1_shape, b_shape] = GetParam(); + + auto param_a0 = std::make_shared(prec, a0_shape); + auto param_a1 = std::make_shared(prec, a1_shape); + auto param_b = std::make_shared(prec, b_shape); + + auto out0 = build_matmul_rmsnorm(prec, param_a0, param_b); + auto out1 = build_matmul_rmsnorm(prec, param_a1, param_b); + + auto concat = std::make_shared(ov::OutputVector{out0, out1}, 2); + auto result = std::make_shared(concat); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{param_a0, param_a1, param_b}, "MatMulRmsnormConcat"); + } +}; + +class MatMulRmsnormConcatBenchmark : public ov::test::BenchmarkLayerTest {}; + +TEST_P(MatMulRmsnormConcatTest, Inference) { + run(); +} +TEST_P(MatMulRmsnormConcatBenchmark, Inference) { + run_benchmark("MLIROp"); +} + +const auto concatParams = + ::testing::Combine(::testing::Values(ov::Shape{1, 128, 1536}), ::testing::Values(ov::Shape{1, 1024, 1536}), ::testing::Values(ov::Shape{1536, 1536})); +INSTANTIATE_TEST_SUITE_P(mlir_MatMulRmsnormConcatTest, MatMulRmsnormConcatTest, concatParams, MatMulRmsnormConcatTest::getTestCaseName); +INSTANTIATE_TEST_SUITE_P(bench_MatMulRmsnormConcatBenchmark, MatMulRmsnormConcatBenchmark, concatParams, MatMulRmsnormConcatTest::getTestCaseName); +} // namespace diff --git a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp index cd011d36c9537a..9ea76b518c4ca1 100644 --- a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp +++ b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp @@ -11,6 +11,7 @@ #include "openvino/op/add.hpp" #include "openvino/op/multiply.hpp" #include "openvino/runtime/intel_gpu/properties.hpp" +#include "transformations/mlir/convert.hpp" namespace { @@ -67,6 +68,8 @@ class ReduceSumSqueezeTest : public testing::WithParamInterface(add_node, mul_val_node); auto result = std::make_shared(mul_node); function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input_node}, "input"); + if (ov::pass::is_mlir_transform_enabled()) + abs_threshold = 0.01f; } void run() override { @@ -80,7 +83,7 @@ class ReduceSumSqueezeTest : public testing::WithParamInterface Date: Fri, 26 Jun 2026 12:35:03 +0000 Subject: [PATCH 074/121] Fix paged-attention link for BUILD_SHARED_LIBS=OFF Signed-off-by: dchigarev --- src/tests/functional/plugin/shared/CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tests/functional/plugin/shared/CMakeLists.txt b/src/tests/functional/plugin/shared/CMakeLists.txt index 6d43f7e9ff84e1..8d84f8876fce11 100644 --- a/src/tests/functional/plugin/shared/CMakeLists.txt +++ b/src/tests/functional/plugin/shared/CMakeLists.txt @@ -65,6 +65,14 @@ ov_add_target( LINK_LIBRARIES PUBLIC openvino::pugixml + # paged_attention_token_type.cpp pulls a non-inline ctor from openvino_reference + # (built STATIC, not re-exported from libopenvino.so). Without this explicit dep, + # GNU ld's single-pass static-archive scan hits libopenvino_reference.a before + # libfuncSharedTests.a and skips the ctor -> undefined symbol. Upstream CI doesn't + # see this because it builds funcSharedTests as a shared lib (BUILD_SHARED_LIBS=ON + # default), where reference is linked into the .so up front. Declaring the dep + # here fixes the ordering for the static-funcSharedTests setup. + openvino::reference common_test_utils func_test_utils ov_lpt_models From 0ccc31d113115a4575505dac57c7d64ed989abe3 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Fri, 26 Jun 2026 12:34:28 +0000 Subject: [PATCH 075/121] Fix missing includes Signed-off-by: dchigarev --- src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index 8121066bdcf9d2..b1dce5f68dfb50 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -158,6 +158,8 @@ #include "transformations/op_conversions/convert_batch_to_space.hpp" #include "transformations/op_conversions/convert_broadcast3.hpp" #include "transformations/op_conversions/convert_depth_to_space.hpp" +#include "transformations/op_conversions/convert_divide.hpp" +#include "transformations/op_conversions/convert_subtract.hpp" #include "transformations/op_conversions/convert_gather_0d.hpp" #include "transformations/op_conversions/convert_gather_downgrade.hpp" #include "transformations/op_conversions/convert_gather_to_compressed.hpp" From 1fa75d8b185f166087e3ab74602973adac8152f3 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Wed, 1 Jul 2026 10:10:27 +0000 Subject: [PATCH 076/121] [MemrefDescriptor] Fix arguments packing for dynamic executor Signed-off-by: dchigarev --- .../src/transformations/mlir/mlir_op.cpp | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 2373d023a3e7a9..bb4040fe2f553a 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -280,16 +280,14 @@ struct MemRefDescriptor { std::vector shape; std::vector strides; - void append_to_packed_args(std::vector& args) { - args.push_back(&allocated); - args.push_back(&aligned); - args.push_back(&offset); - for (size_t i = 0; i < shape.size(); ++i) { - args.push_back(&shape[i]); - } - for (size_t i = 0; i < strides.size(); ++i) { - args.push_back(&strides[i]); - } + // Pack into a fixed-stride layout consumed by MLIREvaluateGcGPU::invoke_packed: + // [aligned, rank, shape*, strides*, is_usm] + void append_to_packed_args(std::vector& args, bool is_usm) { + args.push_back(aligned); + args.push_back(reinterpret_cast(shape.size())); + args.push_back(shape.data()); + args.push_back(strides.data()); + args.push_back(reinterpret_cast(static_cast(is_usm))); } }; @@ -396,18 +394,18 @@ bool MLIREvaluateGcGPU::invoke_packed(std::vector& args, const ov::Evalua gc::gpu::OclContext ctx = build_ocl_context(evaluationContext); gc::gpu::DynamicExecutor exec(module); - auto it = evaluationContext.find(ov::internal::mlir_meta::is_kernel_arg_usm.name()); - if (it == evaluationContext.end()) { - OPENVINO_THROW("No is_kernel_arg_usm provided for OpenCL execution"); - } - std::vector argTypes = it->second.as>(); - for (size_t i = 0; i < args.size(); i+=4) { + // Layout (5 pointers per memref, see MemRefDescriptor::append_to_packed_args): + // [aligned, rank, shape*, strides*, is_usm] + constexpr size_t kStride = 5; + OPENVINO_ASSERT(args.size() % kStride == 0, + "[GPU] MLIREvaluateGcGPU::invoke_packed: malformed args vector"); + for (size_t i = 0; i < args.size(); i += kStride) { exec.arg( /*alignedPtr=*/args[i], /*rank=*/reinterpret_cast(args[i + 1]), /*shape=*/reinterpret_cast(args[i + 2]), /*strides=*/reinterpret_cast(args[i + 3]), - /*isUsm=*/argTypes[i] + /*isUsm=*/reinterpret_cast(args[i + 4]) != 0 ); } exec(ctx); @@ -521,9 +519,10 @@ bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs, } std::vector memref_args; + memref_args.reserve(inputs.size() + outputs.size()); for (size_t i = 0; i < inputs.size(); ++i) { auto& initial_shape = get_input_partial_shape(i); - memref_args.push_back(MemRefDescriptor(inputs[i], initial_shape)); + memref_args.emplace_back(inputs[i], initial_shape); } for (size_t i = 0; i < outputs.size(); ++i) { // TODO: Optimize by adding all dimensions to dimensions_map, not only dynamic @@ -539,17 +538,25 @@ bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs, target.push_back(dim.get_length()); } } - //std::cerr << "[ DEBUG ] Set outputs[" << i << "].shape(" << target << ")\n"; outputs[i].set_shape(target); - memref_args.push_back(MemRefDescriptor(outputs[i])); + memref_args.emplace_back(outputs[i]); } - std::vector args; - std::for_each(memref_args.begin(), memref_args.end(), [&args](MemRefDescriptor& x) { - x.append_to_packed_args(args); - }); + std::vector is_usm; + auto it = evaluationContext.find(ov::internal::mlir_meta::is_kernel_arg_usm.name()); + if (it != evaluationContext.end()) { + is_usm = it->second.as>(); + } else { + is_usm.assign(memref_args.size(), false); + } + OPENVINO_ASSERT(is_usm.size() == memref_args.size(), + "[GPU] MLIROp::evaluate: is_usm and memref count mismatch"); + + std::vector args; + for (size_t k = 0; k < memref_args.size(); ++k) { + memref_args[k].append_to_packed_args(args, is_usm[k]); + } - //std::cerr << "[ INFO ] Running kernel in MLIROp::evaluate\n"; return engine->invoke_packed(args, evaluationContext); } From e7de9c5cd7b45303c05e9da06e91aab8356a1ae6 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Wed, 1 Jul 2026 11:22:26 +0000 Subject: [PATCH 077/121] [MLIROp] Fix shape_infer for dynamic shapes Signed-off-by: dchigarev --- .../include/transformations/mlir/convert.hpp | 13 +++ .../src/transformations/mlir/mlir_op.cpp | 51 +++++++++++- .../src/transformations/mlir/mlir_op.hpp | 1 + .../intel_gpu/src/plugin/ops/mlir_op.cpp | 8 +- .../tests/functional/mlir_op/matmul.cpp | 81 +++++++++++++++++++ .../tests/functional/mlir_op/sdpa.cpp | 34 +++++++- 6 files changed, 181 insertions(+), 7 deletions(-) diff --git a/src/common/transformations/include/transformations/mlir/convert.hpp b/src/common/transformations/include/transformations/mlir/convert.hpp index 700e27a4e34898..0c7dc4920dd65e 100644 --- a/src/common/transformations/include/transformations/mlir/convert.hpp +++ b/src/common/transformations/include/transformations/mlir/convert.hpp @@ -5,6 +5,7 @@ #pragma once #include "openvino/core/model.hpp" +#include "openvino/core/partial_shape.hpp" #include "openvino/util/env_util.hpp" #include "transformations_visibility.hpp" @@ -19,5 +20,17 @@ inline bool is_mlir_transform_enabled() { void TRANSFORMATIONS_API transformMLIR(std::shared_ptr model, std::shared_ptr loweringContext); +} + +namespace mlir { + +// Resolves dynamic dims of MLIROp output shapes from runtime input shapes via +// the op's dimensions_map. Asserts if the node is not an MLIROp. +// Exposed here so callers outside transformations (e.g. Intel GPU plugin) can +// invoke shape inference without depending on the private MLIROp header. +std::vector TRANSFORMATIONS_API mlir_op_shape_infer( + const std::shared_ptr& op, + const std::vector& input_shapes); + } } diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index 2373d023a3e7a9..b03f99dec727bd 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -3,6 +3,7 @@ // #include "mlir_op.hpp" +#include "transformations/mlir/convert.hpp" #include #include @@ -504,10 +505,50 @@ MLIROp::MLIROp(const ov::OutputVector& args, std::shared_ptr e constructor_validate_and_infer_types(); } +std::vector MLIROp::shape_infer(const std::vector& input_shapes) const { + OPENVINO_ASSERT(dimensions_map.size() == output_types.size(), + "MLIROp::shape_infer: dimensions_map size (", dimensions_map.size(), + ") does not match output_types size (", output_types.size(), ")"); + + std::vector output_shapes; + output_shapes.reserve(output_types.size()); + for (size_t i = 0; i < output_types.size(); ++i) { + ov::PartialShape resolved = std::get<1>(output_types[i]); + OPENVINO_ASSERT(dimensions_map[i].size() == resolved.size(), + "MLIROp::shape_infer: dimensions_map[", i, "] size (", dimensions_map[i].size(), + ") does not match output ", i, " rank (", resolved.size(), ")"); + + for (size_t j = 0; j < resolved.size(); ++j) { + if (!resolved[j].is_dynamic()) { + continue; + } + size_t input_index, dim_index; + std::tie(input_index, dim_index) = dimensions_map[i][j]; + OPENVINO_ASSERT(input_index < input_shapes.size(), + "MLIROp::shape_infer: dimensions_map[", i, "][", j, "] refers to input ", + input_index, " but only ", input_shapes.size(), " input shapes provided"); + OPENVINO_ASSERT(dim_index < input_shapes[input_index].size(), + "MLIROp::shape_infer: dimensions_map[", i, "][", j, "] refers to dim ", + dim_index, " of input ", input_index, " (rank ", + input_shapes[input_index].size(), ")"); + resolved[j] = input_shapes[input_index][dim_index]; + } + output_shapes.push_back(resolved); + } + return output_shapes; +} + void MLIROp::validate_and_infer_types() { + std::vector input_shapes; + input_shapes.reserve(get_input_size()); + for (size_t i = 0; i < get_input_size(); ++i) { + input_shapes.push_back(get_input_partial_shape(i)); + } + auto output_shapes = shape_infer(input_shapes); + set_output_size(output_types.size()); for (size_t i = 0; i < output_types.size(); ++i) { - set_output_type(i, std::get<0>(output_types[i]), std::get<1>(output_types[i])); + set_output_type(i, std::get<0>(output_types[i]), output_shapes[i]); } } @@ -561,5 +602,13 @@ bool MLIROp::has_evaluate() const { return true; } +std::vector mlir_op_shape_infer( + const std::shared_ptr& op, + const std::vector& input_shapes) { + auto mlir_op = std::dynamic_pointer_cast(op); + OPENVINO_ASSERT(mlir_op != nullptr, "mlir_op_shape_infer expects an MLIROp node"); + return mlir_op->shape_infer(input_shapes); +} + } // namespace mlir } // namespace ov \ No newline at end of file diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.hpp b/src/common/transformations/src/transformations/mlir/mlir_op.hpp index e08072e2306125..da22f10d65a051 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.hpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.hpp @@ -103,6 +103,7 @@ class OPENVINO_API MLIROp : public ov::op::Op { bool evaluate(ov::TensorVector& output_values, const ov::TensorVector& input_values, const ov::EvaluationContext& evaluationContext) const override; bool has_evaluate() const override; + std::vector shape_infer(const std::vector& input_shapes) const; }; } // namespace mlir diff --git a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp index c9963cfb92c017..177e52ef783aff 100644 --- a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp +++ b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp @@ -12,6 +12,8 @@ #include "openvino/runtime/intel_gpu/remote_properties.hpp" #include "openvino/runtime/internal_properties.hpp" +#include "transformations/mlir/convert.hpp" + namespace ov { namespace op { namespace mlir { @@ -117,11 +119,7 @@ void CreateMLIRSubgraphOp(ProgramBuilder& p, const std::shared_ptr& input_shapes) -> std::vector { - std::vector output_shapes; - for (size_t i = 0, n = op->get_output_size(); i < n; ++i) { - output_shapes.push_back(op->get_output_partial_shape(i)); - } - return output_shapes; + return ov::mlir::mlir_op_shape_infer(op, input_shapes); }; auto inputs = p.GetInputInfo(op); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp index 04610dcca14cab..3657defb5bc854 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp @@ -4,6 +4,7 @@ #include "openvino/op/matmul.hpp" +#include "common_test_utils/ov_tensor_utils.hpp" #include "openvino/op/parameter.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" @@ -53,4 +54,84 @@ INSTANTIATE_TEST_SUITE_P(mlir_BatchMatMul, ::testing::Values(ov::element::f16)), BatchMatMulTest::getTestCaseName); +// -------- Dynamic-shape MatMul (non-batched, dynamic K) -------- + +using DynamicMatMulParams = std::tuple; + +class DynamicMatMulTest : public testing::WithParamInterface, + virtual public ov::test::SubgraphBaseTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [a_shape, b_shape, prec] = obj.param; + std::ostringstream result; + result << "A_IS=" << ov::test::utils::partialShape2str({a_shape.first}) << "_"; + result << "A_TS="; + for (const auto& s : a_shape.second) { + result << ov::test::utils::vec2str(s) << "_"; + } + result << "B_IS=" << ov::test::utils::partialShape2str({b_shape.first}) << "_"; + result << "B_TS="; + for (const auto& s : b_shape.second) { + result << ov::test::utils::vec2str(s) << "_"; + } + result << "precision=" << prec; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto& [a_shape, b_shape, prec] = GetParam(); + init_input_shapes({a_shape, b_shape}); + auto param_a = std::make_shared(prec, inputDynamicShapes[0]); + auto param_b = std::make_shared(prec, inputDynamicShapes[1]); + auto matmul = std::make_shared(param_a, param_b, false, false); + auto result = std::make_shared(matmul); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{param_a, param_b}, "DynamicMatMul"); + abs_threshold = 0.05f; + rel_threshold = 0.05f; + } + + void generate_inputs(const std::vector& target_input_shapes) override { + const auto& model_inputs = function->inputs(); + inputs.clear(); + ov::test::utils::InputGenerateData gen(-0.5, 1, 4); + for (size_t i = 0; i < model_inputs.size(); ++i) { + auto tensor = ov::test::utils::create_and_fill_tensor( + model_inputs[i].get_element_type(), target_input_shapes[i], gen); + inputs.insert({model_inputs[i].get_node_shared_ptr(), tensor}); + } + } +}; + +TEST_P(DynamicMatMulTest, Inference) { + run(); +} + +// A: (M=1024, K=?) x B: (K=?, N=1536), K varies between iterations. +INSTANTIATE_TEST_SUITE_P(mlir_DynamicMatMul_dynamicK, + DynamicMatMulTest, + ::testing::Combine(::testing::Values(ov::test::InputShape{ + ov::PartialShape{1024, -1}, + {ov::Shape{1024, 768}, ov::Shape{1024, 1536}}}), + ::testing::Values(ov::test::InputShape{ + ov::PartialShape{-1, 1536}, + {ov::Shape{768, 1536}, ov::Shape{1536, 1536}}}), + ::testing::Values(ov::element::f16)), + DynamicMatMulTest::getTestCaseName); + +// A: (M=?, K=?) x B: (K=?, N=?), all dims dynamic, all vary between iterations. +INSTANTIATE_TEST_SUITE_P(mlir_DynamicMatMul_allDynamic, + DynamicMatMulTest, + ::testing::Combine(::testing::Values(ov::test::InputShape{ + ov::PartialShape{-1, -1}, + {ov::Shape{512, 768}, ov::Shape{1024, 1536}, ov::Shape{128, 256}}}), + ::testing::Values(ov::test::InputShape{ + ov::PartialShape{-1, -1}, + {ov::Shape{768, 384}, ov::Shape{1536, 1024}, ov::Shape{256, 512}}}), + ::testing::Values(ov::element::f16)), + DynamicMatMulTest::getTestCaseName); + } // namespace diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp index f7bb0012dbe011..c2544bd36363da 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp @@ -268,7 +268,7 @@ void ScaledAttnLayerGPUMlirTest::generate_inputs(const std::vector& t { for (int i = 0; i < 3; ++i) { shapes[i] = targetInputStaticShapes[i]; - ov::test::utils::InputGenerateData data(-1, 1, 64); + ov::test::utils::InputGenerateData data(-0.5, 1, 4); ov::Tensor data_tensor = ov::test::utils::create_and_fill_tensor(ov::element::f16, shapes[i], data); inputs.insert({model_inputs[i].get_node_shared_ptr(), data_tensor}); } @@ -417,4 +417,36 @@ INSTANTIATE_TEST_SUITE_P(smoke_ScaledAttnStatic4D_GPU, static_shape_params_4D, ScaledAttnLayerGPUMlirTest::getTestCaseName); +const std::vector> dynamic_shapes_4D{ + { + // q shape: ?x24x?x64 + {ov::test::InputShape{ov::PartialShape{-1, 24, -1, 64}, + {ov::Shape{1, 24, 128, 64}}} + }, + // k shape: ?x24x?x64 + {ov::test::InputShape{ov::PartialShape{-1, 24, -1, 64}, + {ov::Shape{1, 24, 128, 64}}} + }, + // v shape: ?x24x?x64 + {ov::test::InputShape{ov::PartialShape{-1, 24, -1, 64}, + {ov::Shape{1, 24, 128, 64}}} + }, + }, +}; + +const auto dynamic_shape_params_4D = testing::Combine(testing::Values(ov::element::f16), + testing::ValuesIn(dynamic_shapes_4D), + testing::Values(false), // is_causal + testing::Values(false), // has_attn + testing::Values(false), // is_attn_const + testing::Values(false), // has_scale + testing::Values(false), // is_scale_const + testing::ValuesIn({disable_transpose}), + testing::Values(false)); // has_sink + +INSTANTIATE_TEST_SUITE_P(smoke_ScaledAttnDynamic4D_GPU, + ScaledAttnLayerGPUMlirTest, + dynamic_shape_params_4D, + ScaledAttnLayerGPUMlirTest::getTestCaseName); + } // namespace From 3ffdd727abf210f50eeca4a2f3bea9bc9e6a9fb6 Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Mon, 6 Jul 2026 15:42:23 +0200 Subject: [PATCH 078/121] Fix submodules versions (#1) Signed-off-by: dchigarev --- src/bindings/python/thirdparty/pybind11 | 2 +- src/plugins/intel_cpu/thirdparty/ComputeLibrary | 2 +- src/plugins/intel_cpu/thirdparty/onednn | 2 +- src/plugins/intel_gpu/thirdparty/onednn_gpu | 2 +- src/plugins/intel_npu/thirdparty/level-zero-ext | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bindings/python/thirdparty/pybind11 b/src/bindings/python/thirdparty/pybind11 index f5fbe867d2d26e..45fab4087eaaff 160000 --- a/src/bindings/python/thirdparty/pybind11 +++ b/src/bindings/python/thirdparty/pybind11 @@ -1 +1 @@ -Subproject commit f5fbe867d2d26e4a0a9177a51f6e568868ad3dc8 +Subproject commit 45fab4087eaaff234227a10cf7845e8b07f28a98 diff --git a/src/plugins/intel_cpu/thirdparty/ComputeLibrary b/src/plugins/intel_cpu/thirdparty/ComputeLibrary index 007264fa740de5..7f8a8ab512ad8d 160000 --- a/src/plugins/intel_cpu/thirdparty/ComputeLibrary +++ b/src/plugins/intel_cpu/thirdparty/ComputeLibrary @@ -1 +1 @@ -Subproject commit 007264fa740de5723ebddef16b7bb3657692c088 +Subproject commit 7f8a8ab512ad8d1c1c207003ac5f96c4445da36f diff --git a/src/plugins/intel_cpu/thirdparty/onednn b/src/plugins/intel_cpu/thirdparty/onednn index c6b79c1207bd5f..f82d833de6f13f 160000 --- a/src/plugins/intel_cpu/thirdparty/onednn +++ b/src/plugins/intel_cpu/thirdparty/onednn @@ -1 +1 @@ -Subproject commit c6b79c1207bd5f20b9395536dab1d71a47cfcb1d +Subproject commit f82d833de6f13fac4bb1926d521ca8fec4f4ae01 diff --git a/src/plugins/intel_gpu/thirdparty/onednn_gpu b/src/plugins/intel_gpu/thirdparty/onednn_gpu index bfea6fd1ae4e82..6569fb284ea8e5 160000 --- a/src/plugins/intel_gpu/thirdparty/onednn_gpu +++ b/src/plugins/intel_gpu/thirdparty/onednn_gpu @@ -1 +1 @@ -Subproject commit bfea6fd1ae4e827ffc1bf12b323d09db5df99313 +Subproject commit 6569fb284ea8e5ec628090a3d1d400485eed84b5 diff --git a/src/plugins/intel_npu/thirdparty/level-zero-ext b/src/plugins/intel_npu/thirdparty/level-zero-ext index 8404c63a88d182..f9ad3bf89c2418 160000 --- a/src/plugins/intel_npu/thirdparty/level-zero-ext +++ b/src/plugins/intel_npu/thirdparty/level-zero-ext @@ -1 +1 @@ -Subproject commit 8404c63a88d182726038d2b07c219731dada9c21 +Subproject commit f9ad3bf89c2418d714aef2e6b96a5aafb12a1971 From eec19f7650627dea8f8ff69a0e022b9f25a17a13 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Wed, 8 Jul 2026 12:16:05 +0200 Subject: [PATCH 079/121] Static build + cleanup (#2) --- CMakeLists.txt | 11 - cmake/graph-compiler.cmake | 27 +- cmake/llvm.cmake | 30 ++ cmake/tpp-mlir.cmake | 53 ---- install_build_dependencies.sh | 16 ++ scripts/build_tpp_mlir.sh | 14 - src/cmake/openvino.cmake | 14 +- src/common/transformations/CMakeLists.txt | 8 +- .../include/transformations/mlir/convert.hpp | 2 +- .../src/transformations/mlir/convert.cpp | 155 +---------- .../src/transformations/mlir/mlir_op.cpp | 262 +----------------- .../src/transformations/mlir/mlir_op.hpp | 31 --- .../transformation_pipeline.cpp | 1 - .../src/plugin/transformations_pipeline.cpp | 2 + 14 files changed, 104 insertions(+), 522 deletions(-) create mode 100644 cmake/llvm.cmake delete mode 100644 cmake/tpp-mlir.cmake delete mode 100755 scripts/build_tpp_mlir.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index b20b72744b3a5b..256db9bd82436e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,21 +142,10 @@ function(ov_developer_package_export_targets) endfunction() -# -# TPP-MLIR -# - -# enables tpp-mlir for temporary MLIR lowering CPU pipeline -# FIXME: Move to all-upsteram lowering into XSMM/DNN/MKL -include(cmake/tpp-mlir.cmake) - # # Graph Compiler # if (ENABLE_GRAPH_COMPILER) - find_package(LLVM REQUIRED CONFIG) - find_package(MLIR REQUIRED CONFIG) - include(cmake/graph-compiler.cmake) add_definitions(-DGRAPH_COMPILER) add_definitions(-DGC_USE_GPU) diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index a99a847ce3daf5..6143572b364e1c 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -1,3 +1,5 @@ +include("${CMAKE_CURRENT_LIST_DIR}/llvm.cmake") + get_property(GRAPH_COMPILER_LIBS GLOBAL PROPERTY GRAPH_COMPILER_LIBS) if (NOT DEFINED GRAPH_COMPILER_LIBS) if (DEFINED GraphCompiler_DIR AND EXISTS "${GraphCompiler_DIR}/GraphCompilerTargets.cmake") @@ -5,13 +7,34 @@ if (NOT DEFINED GRAPH_COMPILER_LIBS) elseif (DEFINED GraphCompiler_ROOT) include("${GraphCompiler_ROOT}/lib/cmake/GraphCompiler/GraphCompilerTargets.cmake") else() - find_package(GraphCompiler REQUIRED) + find_package(GraphCompiler QUIET) + if (NOT GraphCompiler_FOUND) + set(GRAPH_COMPILER_REPO "https://github.com/intel-sandbox/graph-compiler" CACHE STRING "GraphCompiler repository URL") + set(GRAPH_COMPILER_TAG "main" CACHE STRING "GraphCompiler git tag/branch") + message(STATUS "GraphCompiler not found, fetching from: ${GRAPH_COMPILER_REPO}") + include(FetchContent) + FetchContent_Declare( + GraphCompiler + GIT_REPOSITORY ${GRAPH_COMPILER_REPO} + GIT_TAG ${GRAPH_COMPILER_TAG} + GIT_SHALLOW TRUE + ) + set(GC_ENABLE_TEST OFF CACHE BOOL "" FORCE) + set(GC_ENABLE_TOOLS OFF CACHE BOOL "" FORCE) + set(GC_ENABLE_PYTHON_BINDINGS OFF CACHE BOOL "" FORCE) + set(GC_DYLINK ${LLVM_DYLINK} CACHE BOOL "" FORCE) + set(_ov_build_shared_libs ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections") + FetchContent_MakeAvailable(GraphCompiler) + set(BUILD_SHARED_LIBS ${_ov_build_shared_libs}) + endif() endif() if(LLVM_DYLINK) set(GRAPH_COMPILER_LIBS GcInterface GraphCompiler) else() - set(GRAPH_COMPILER_LIBS GcInterface MLIRLinalgx GcGpuOclRuntime) + set(GRAPH_COMPILER_LIBS GcInterface MLIRLinalgx GcGpuOclRuntime GcGpuPasses GcGpuOclPasses) endif() set_property(GLOBAL PROPERTY GRAPH_COMPILER_LIBS ${GRAPH_COMPILER_LIBS}) endif () diff --git a/cmake/llvm.cmake b/cmake/llvm.cmake new file mode 100644 index 00000000000000..cabcd7778ea485 --- /dev/null +++ b/cmake/llvm.cmake @@ -0,0 +1,30 @@ +include_guard() + +find_package(LLVM CONFIG QUIET) +find_package(MLIR CONFIG QUIET) + +if (NOT LLVM_FOUND OR NOT MLIR_FOUND) + # Try apt.llvm.org install paths explicitly + set(LLVM_VERSION "23" CACHE STRING "LLVM nightly major version from apt.llvm.org") + set(llvm_apt_dir "/usr/lib/llvm-${LLVM_VERSION}/lib/cmake/llvm") + set(mlir_apt_dir "/usr/lib/llvm-${LLVM_VERSION}/lib/cmake/mlir") + + if (EXISTS "${llvm_apt_dir}/LLVMConfig.cmake" AND EXISTS "${mlir_apt_dir}/MLIRConfig.cmake") + set(LLVM_DIR "${llvm_apt_dir}" CACHE PATH "" FORCE) + set(MLIR_DIR "${mlir_apt_dir}" CACHE PATH "" FORCE) + else() + message(FATAL_ERROR + "LLVM/MLIR not found. Either install LLVM nightly:\n" + " sudo ./install_build_dependencies.sh -llvm\n" + "Or add the following CMake options:\n" + " -DLLVM_DIR=path/to/llvm/lib/cmake/llvm\n" + " -DMLIR_DIR=path/to/llvm/lib/cmake/mlir\n" + ) + endif() +endif() + +find_package(LLVM REQUIRED CONFIG) +find_package(MLIR REQUIRED CONFIG) + +message(STATUS "LLVM ${LLVM_PACKAGE_VERSION} at ${LLVM_DIR}") +message(STATUS "MLIR at ${MLIR_DIR}") diff --git a/cmake/tpp-mlir.cmake b/cmake/tpp-mlir.cmake deleted file mode 100644 index d988ac7a919271..00000000000000 --- a/cmake/tpp-mlir.cmake +++ /dev/null @@ -1,53 +0,0 @@ -# If TPP-MLIR is in library path, add it to the dependencies -# This should be the build directory, not the source or the 'lib' -# FIXME: Make this an actual CMake discovery -if (TPP_MLIR_DIR) - message(STATUS "TPP-MLIR at ${TPP_MLIR_DIR}") - add_compile_definitions(TPP_MLIR) - set(TPP_MLIR_LIBS - # Keep the next two libs at the top of the list to avoid undefined references at link time - TPPPipeline - TPPPassBundles - TPPCheckDialect - TPPCheckToLoops - TPPGPU - TPPIR - TPPLinalgToFunc - TPPLinalgToXSMM - TPPPerfDialect - TPPPerfToFunc - TPPPerfToLoop - TPPRunner - TPPTestLib - TPPTransforms - TPPTransformsUtils - TPPXsmmDialect - TPPXsmmToFunc - xsmm - tpp_xsmm_runner_utils - ) - function(add_tpp_mlir_includes target) - target_include_directories(${target} PRIVATE ${TPP_MLIR_DIR}/../include ${TPP_MLIR_DIR}/include) - endfunction() - function(add_tpp_mlir_libs target) - target_link_directories(${target} PRIVATE ${TPP_MLIR_DIR}/lib) - target_link_libraries(${target} PRIVATE ${TPP_MLIR_LIBS}) - target_link_options(${target} PRIVATE - -Wl,--no-as-needed - -L${TPP_MLIR_DIR}/lib - -ltpp_xsmm_runner_utils - -L${LLVM_LIBRARY_DIR} - -lmlir_c_runner_utils - -Wl,--as-needed - ) - #FIXME: Provide platform-independent way of doing that: - install(FILES ${TPP_MLIR_DIR}/lib/libtpp_xsmm_runner_utils.so ${TPP_MLIR_DIR}/lib/libtpp_xsmm_runner_utils.so.19.0git DESTINATION ${OV_CPACK_RUNTIMEDIR}) - endfunction() -else() - function(add_tpp_mlir_includes target) - message(DEBUG "TPP-MLIR not enabled, skipping ${target}") - endfunction() - function(add_tpp_mlir_libs target) - message(DEBUG "TPP-MLIR not enabled, skipping ${target}") - endfunction() -endif() diff --git a/install_build_dependencies.sh b/install_build_dependencies.sh index 9cf0cdadc1e9be..cf881ce55c905e 100755 --- a/install_build_dependencies.sh +++ b/install_build_dependencies.sh @@ -26,6 +26,7 @@ if [ -f /etc/lsb-release ] || [ -f /etc/debian_version ] ; then apt update apt-get install -y --no-install-recommends \ + software-properties-common \ `# for python3-pip` \ ca-certificates \ file \ @@ -89,6 +90,21 @@ if [ -f /etc/lsb-release ] || [ -f /etc/debian_version ] ; then else apt-get install -y --no-install-recommends nlohmann-json-dev fi + + # LLVM/MLIR nightly from apt.llvm.org + for arg in "$@"; do + if [ "$arg" = "-llvm" ]; then + LLVM_VERSION=$(grep -Po '(?<=set\(LLVM_VERSION ")[^"]*' "$(dirname "$0")/cmake/llvm.cmake") + if ! dpkg -l "libmlir-${LLVM_VERSION}-dev" &>/dev/null; then + wget -qO- https://apt.llvm.org/llvm.sh | bash -s -- "${LLVM_VERSION}" all + apt-get install -y --no-install-recommends \ + "libmlir-${LLVM_VERSION}-dev" "mlir-${LLVM_VERSION}-tools" \ + `# LLVMExports.cmake requires zstd::libzstd_shared` \ + libzstd-dev + fi + break + fi + done elif [ -f /etc/redhat-release ] || grep -q "rhel\|tencentos\|opencloudos" /etc/os-release ; then yum update # RHEL 8 / CentOS 7 / Fedora 29 diff --git a/scripts/build_tpp_mlir.sh b/scripts/build_tpp_mlir.sh deleted file mode 100755 index 52e5d6b05d1181..00000000000000 --- a/scripts/build_tpp_mlir.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# Run it in tpp-mlir/build subdirectory -# Set CUSTOM_LLVM_ROOT to llvm-project/build directory - -cmake -G Ninja .. \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DMLIR_DIR=$CUSTOM_LLVM_ROOT/lib/cmake/mlir \ - -DLLVM_EXTERNAL_LIT=$CUSTOM_LLVM_ROOT/bin/llvm-lit \ - -DCMAKE_C_COMPILER=clang \ - -DCMAKE_CXX_COMPILER=clang++ \ - -DLLVM_USE_LINKER=lld - -cmake --build . --target check-tpp \ No newline at end of file diff --git a/src/cmake/openvino.cmake b/src/cmake/openvino.cmake index 84a947e5463a42..4fc60b414e000b 100644 --- a/src/cmake/openvino.cmake +++ b/src/cmake/openvino.cmake @@ -49,12 +49,12 @@ target_include_directories(${TARGET_NAME} INTERFACE $ $) -find_package(MLIR REQUIRED CONFIG) +if(ENABLE_GRAPH_COMPILER) + find_package(MLIR REQUIRED CONFIG) -if (LLVM_DYLINK) - set(MLIR_ALL_LIBS LLVM MLIR) -else() - get_property(MLIR_ALL_LIBS GLOBAL PROPERTY MLIR_ALL_LIBS) + if (LLVM_DYLINK) + set(MLIR_ALL_LIBS LLVM MLIR) + endif() endif() target_link_libraries(${TARGET_NAME} @@ -68,7 +68,9 @@ target_link_libraries(${TARGET_NAME} PUBLIC $<$,$,9.1>>:stdc++fs> $<$,$,9.0>>:c++fs>) -add_tpp_mlir_libs(${TARGET_NAME}) +if(ENABLE_GRAPH_COMPILER AND NOT LLVM_DYLINK) + target_link_options(${TARGET_NAME} PRIVATE -Wl,--gc-sections) +endif() if(BUILD_SHARED_LIBS) target_link_libraries(${TARGET_NAME} PRIVATE openvino::shutdown) diff --git a/src/common/transformations/CMakeLists.txt b/src/common/transformations/CMakeLists.txt index 1906774e4f186c..29cfd6f655b702 100644 --- a/src/common/transformations/CMakeLists.txt +++ b/src/common/transformations/CMakeLists.txt @@ -9,7 +9,12 @@ set(PUBLIC_HEADERS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") file(GLOB_RECURSE LIBRARY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) file(GLOB_RECURSE PUBLIC_HEADERS ${PUBLIC_HEADERS_DIR}/*.hpp) -find_package(MLIR REQUIRED CONFIG) +if(NOT ENABLE_GRAPH_COMPILER) + file(GLOB_RECURSE MLIR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/transformations/mlir/*.cpp) + list(REMOVE_ITEM LIBRARY_SRC ${MLIR_SRC}) +else() + find_package(MLIR REQUIRED CONFIG) +endif() # Create named folders for the sources within the .vcproj # Empty name lists them directly under the .vcproj @@ -47,7 +52,6 @@ target_include_directories(${TARGET_NAME}_obj PRIVATE "${PUBLIC_HEADERS_DIR}" "${GRAPH_COMPILER_INCLUDES}") target_compile_options(${TARGET_NAME}_obj PUBLIC ${GRAPH_COMPILER_COMPILE_OPTIONS}) -add_tpp_mlir_includes(${TARGET_NAME}_obj) ov_add_clang_format_target(${TARGET_NAME}_clang FOR_TARGETS ${TARGET_NAME}_obj) diff --git a/src/common/transformations/include/transformations/mlir/convert.hpp b/src/common/transformations/include/transformations/mlir/convert.hpp index 0c7dc4920dd65e..f3dfaf9f0337c5 100644 --- a/src/common/transformations/include/transformations/mlir/convert.hpp +++ b/src/common/transformations/include/transformations/mlir/convert.hpp @@ -14,7 +14,7 @@ namespace ov { namespace pass { inline bool is_mlir_transform_enabled() { - return !util::getenv_string("OV_MLIR_MODE").empty() && util::getenv_bool("OV_MLIR", true); + return util::getenv_bool("OV_MLIR", false); } void TRANSFORMATIONS_API transformMLIR(std::shared_ptr model, diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/common/transformations/src/transformations/mlir/convert.cpp index c772869e49676b..f57db096bee9b1 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/common/transformations/src/transformations/mlir/convert.cpp @@ -45,7 +45,6 @@ #include "mlir/Dialect/Arith/IR/Arith.h" #include "mlir/Dialect/Arith/Transforms/Passes.h" #include "mlir/Dialect/Bufferization/Transforms/Passes.h" -#include "mlir/Dialect/DLTI/DLTI.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/Linalg/Passes.h" @@ -75,16 +74,7 @@ #include "mlir/Target/LLVMIR/Export.h" #include "mlir/Target/LLVMIR/ModuleTranslation.h" -#ifdef GRAPH_COMPILER #include "gc/Transforms/Passes.h" -#endif - -#ifdef TPP_MLIR // If TPP is available -#include "TPP/Dialect/Check/CheckDialect.h" -#include "TPP/Dialect/Perf/PerfDialect.h" -#include "TPP/Dialect/Xsmm/XsmmDialect.h" -#include "TPP/GPU/Utils.h" -#endif #include "mlir_op.hpp" #include "conversion/patterns.hpp" @@ -182,22 +172,12 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, auto func = moduleBuilder.create(funcLoc, function_name, funcType); auto block_builder = mlir::OpBuilder::atBlockBegin(func.addEntryBlock() /* TODO: Add logger here */); - // Affix target information attribute to the module to be used, at its discretion, - // by the MLIR-compiler that consumes this module. - auto tileSize = IntegerAttr::get(IntegerType::get(context, 32), 32); - auto key = StringAttr::get(context, "tile_size"); - DataLayoutEntryInterface entry = DataLayoutEntryAttr::get(context, key, tileSize); - TargetDeviceSpecInterface deviceSpec = TargetDeviceSpecAttr::get(context, ArrayRef(entry)); - auto deviceStr = StringAttr::get(context, "CPU"); - auto sysSpec = TargetSystemSpecAttr::get(context, {DataLayoutEntryAttr::get(deviceStr, deviceSpec)}); - module.getOperation()->setAttr("#dlti.sys_spec", sysSpec); - GraphConverter graph_converter(context, &block_builder); for (size_t i = 0, r = 0; i < inputs.size(); ++i) { auto loc = createLocation(context, inputs[i].get_node_shared_ptr()); if (is_constant[i]) { - auto* cst = ov::as_type(inputs[i].get_node()); + auto* cst = ov::as_type(inputs[i].get_node()); graph_converter.nodeOutputMap.emplace(inputs[i], getConstant(block_builder, cst, loc)); } else { auto funcInputVal = func.getArgument(r++); @@ -266,7 +246,6 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, // This pass converts a group of nodes into a single MLIROp NodePtr ngraph_to_mlir_op(MLIRContext* context, SubgraphPtr subgraph, - MlirMode mode, std::shared_ptr loweringContext) { SmallVector keptInputIndices; mlir::OwningOpRef module = @@ -320,7 +299,7 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, } return std::make_shared( inputs, - MLIREvaluate::create(std::move(module), mode, loweringContext), + std::make_shared(std::move(module), loweringContext), output_types, output_map ); @@ -341,20 +320,18 @@ void replace_subgraph(SubgraphPtr subgraph, NodePtr node) { class Partitioner : public ov::pass::ModelPass { MLIRContext* context; - MlirMode mode; std::shared_ptr loweringContext; public: OPENVINO_RTTI("Partitioner"); - Partitioner(MLIRContext* context, MlirMode mode, std::shared_ptr loweringContext) : + Partitioner(MLIRContext* context, std::shared_ptr loweringContext) : context(context), - mode(mode), loweringContext(loweringContext) {} bool run_on_model(const std::shared_ptr& model) override { SubgraphTracker tracker([this](SubgraphPtr subgraph) { - auto mlir_op = ngraph_to_mlir_op(context, subgraph, mode, loweringContext); + auto mlir_op = ngraph_to_mlir_op(context, subgraph, loweringContext); replace_subgraph(subgraph, mlir_op); OPENVINO_MLIR_DEBUG_PRINT("Created MLIR op: " << mlir_op << "\n"); } @@ -376,7 +353,6 @@ class Partitioner : public ov::pass::ModelPass { void injectMLIR(std::shared_ptr model, MLIRContext* context, - MlirMode mode, std::shared_ptr loweringContext) { ov::pass::Manager manager; using namespace ov::op; @@ -411,7 +387,7 @@ void injectMLIR(std::shared_ptr model, manager.register_pass(); manager.register_pass(); manager.register_pass(); - manager.register_pass(context, mode, loweringContext); + manager.register_pass(context, loweringContext); manager.run_passes(model); model->validate_nodes_and_infer_types(); } @@ -420,71 +396,16 @@ void loadDialects(MLIRContext* context) { context->loadAllAvailableDialects(); } -MLIRContext* get_shared_mlir_context(MlirMode mode) { +MLIRContext* get_shared_mlir_context() { // Gives MLIRContext instance shared for entire OV process and initialized once upon the initial request // FIXME: Bind with OpenVINO lifetime in the sutable class instead of dirty tricking with static lifetime - static std::shared_ptr context; - static bool current_mode = mode; - - if (context) { - if (current_mode != mode) { - OPENVINO_MLIR_DEBUG_PRINT("[ DEBUG ] Switching MLIR mode to: "); - current_mode = mode; - } else { - return context.get(); - } - } else { - OPENVINO_MLIR_DEBUG_PRINT("[ DEBUG ] MLIR mode: "); - } - -#ifdef GRAPH_COMPILER - if (mode == MLIR_MODE_GC || mode == MLIR_MODE_GC_GPU) { - OPENVINO_MLIR_DEBUG_PRINT("GC\n"); - context = std::make_shared(gc::getDialectRegistry()); - } else { -#endif - // Initialize the LLVM machinery - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); -#ifdef TPP_MLIR - if (mode == MLIR_MODE_TPP) { - OPENVINO_MLIR_DEBUG_PRINT("TPP\n"); - // Initialize GPU-related LLVM machinery - tpp::initializeGpuTargets(); - } else { -#endif - assert(mode == MLIR_MODE_DEFAULT); - OPENVINO_MLIR_DEBUG_PRINT("DEFAULT\n"); -#ifdef TPP_MLIR - } -#endif - - // Add the following to include *all* MLIR Core dialects, or selectively - // include what you need like above. You only need to register dialects that - // will be *parsed* by the tool, not the one generated - DialectRegistry registry; -#ifdef TPP_MLIR - if (mode == MLIR_MODE_TPP) { - registry.insert(); - registry.insert(); - registry.insert(); - } -#endif - - registerAllDialects(registry); - registerAllExtensions(registry); - registerAllToLLVMIRTranslations(registry); - mlir::linalg::registerTransformDialectExtension(registry); - mlir::tensor::registerTransformDialectExtension(registry); - - context = std::make_shared(registry); + static std::shared_ptr context = [] { + auto ctx = std::make_shared(gc::getDialectRegistry()); + loadDialects(ctx.get()); + return ctx; + }(); -#ifdef GRAPH_COMPILER - } -#endif - - loadDialects(context.get()); return context.get(); } @@ -492,57 +413,7 @@ MLIRContext* get_shared_mlir_context(MlirMode mode) { void ov::pass::transformMLIR(std::shared_ptr model, std::shared_ptr loweringContext) { - if(is_mlir_transform_enabled()) { - const char *default_mode = -#ifdef TPP_MLIR - "TPP"; -#elif defined(GRAPH_COMPILER) - "GC"; -#else - "DEFAULT"; -#endif - auto mode_str = util::getenv_string("OV_MLIR_MODE"); - - if (mode_str == "") { - mode_str = default_mode; - } else { - // Convert to uppercase - std::transform(mode_str.begin(), mode_str.end(), mode_str.begin(), ::toupper); - } - - MlirMode mode; - - if (mode_str == "TPP") { -#ifndef TPP_MLIR - OPENVINO_THROW( - "[ ERROR ] OpenVINO wasn't compiled with TPP_MLIR support, " - "but OV_MLIR_MODE environment variable is set to TPP."); -#endif - mode = MLIR_MODE_TPP; - } else if (mode_str == "GC") { -#ifndef GRAPH_COMPILER - OPENVINO_THROW( - "[ ERROR ] OpenVINO wasn't compiled with GRAPH_COMPILER support, " - "but OV_MLIR_MODE environment variable is set to GC."); -#endif - mode = MLIR_MODE_GC; - } else if (mode_str == "GC_GPU") { -#ifndef GRAPH_COMPILER - OPENVINO_THROW( - "[ ERROR ] OpenVINO wasn't compiled with GRAPH_COMPILER support, " - "but OV_MLIR_MODE environment variable is set to GC_GPU."); -#endif -#ifndef GC_USE_GPU - OPENVINO_THROW( - "[ ERROR ] GraphCompiler wasn't compiled with Graph Compiler support (-DENABLE_GRAPH_COMPILER), " - "but OV_MLIR_MODE environment variable is set to GC_GPU."); -#endif - mode = MLIR_MODE_GC_GPU; - } else { - OPENVINO_ASSERT(mode_str == "DEFAULT"); - mode = MLIR_MODE_DEFAULT; - } - - injectMLIR(model, get_shared_mlir_context(mode), mode, loweringContext); + if (is_mlir_transform_enabled()) { + injectMLIR(model, get_shared_mlir_context(), loweringContext); } } diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/common/transformations/src/transformations/mlir/mlir_op.cpp index dfe5b995c66550..8ba83474f9a89a 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.cpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.cpp @@ -17,62 +17,19 @@ // TODO: Prune unused headers -- it's hard to understand needed ones #include "llvm/MC/TargetRegistry.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" -#include "llvm/Support/TargetSelect.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Target/TargetOptions.h" -#include "mlir/Dialect/Arith/IR/Arith.h" -#include "mlir/Dialect/Arith/Transforms/Passes.h" -#include "mlir/Transforms/Passes.h" -#include "mlir/Conversion/Passes.h" #include "mlir/Dialect/MemRef/Transforms/Passes.h" #include "mlir/Dialect/Bufferization/Transforms/Passes.h" -#include "mlir/Dialect/Func/IR/FuncOps.h" -#include "mlir/Dialect/LLVMIR/LLVMDialect.h" -#include "mlir/Dialect/Linalg/Passes.h" #include "mlir/Dialect/Linalg/TransformOps/DialectExtension.h" -#include "mlir/Dialect/MemRef/IR/MemRef.h" -#include "mlir/Dialect/Tensor/IR/Tensor.h" -#include "mlir/Dialect/Tensor/TransformOps/TensorTransformOps.h" -#include "mlir/Dialect/Vector/IR/VectorOps.h" -#include "mlir/ExecutionEngine/ExecutionEngine.h" #include "mlir/ExecutionEngine/JitRunner.h" -#include "mlir/ExecutionEngine/OptUtils.h" -#include "mlir/IR/BuiltinAttributes.h" -#include "mlir/IR/BuiltinTypes.h" -#include "mlir/IR/Diagnostics.h" -#include "mlir/IR/Dialect.h" -#include "mlir/IR/Location.h" -#include "mlir/IR/ValueRange.h" -#include "mlir/InitAllDialects.h" -#include "mlir/InitAllExtensions.h" -#include "mlir/InitAllPasses.h" -#include "mlir/Parser/Parser.h" -#include "mlir/Pass/Pass.h" #include "mlir/Pass/PassManager.h" #include "mlir/Support/LLVM.h" -#include "mlir/Target/LLVMIR/Dialect/All.h" -#include "mlir/Target/LLVMIR/Export.h" -#include "mlir/Target/LLVMIR/ModuleTranslation.h" -#ifdef TPP_MLIR // If TPP is available -#include "TPP/PassBundles.h" -#include "TPP/Passes.h" -#endif - -#ifdef GRAPH_COMPILER #include "gc/Transforms/Passes.h" - -#ifdef GC_USE_GPU #include "gc/Utils/Error.h" #include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" #include "openvino/runtime/intel_gpu/remote_properties.hpp" #include "openvino/runtime/internal_properties.hpp" -#endif -#endif namespace { @@ -81,91 +38,11 @@ using namespace mlir; using NodePtr = std::shared_ptr; using SymbolPtr = std::shared_ptr; -void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module, ov::mlir::MlirMode mode) { +void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module) { PassManager pm(module->getContext()); - switch (mode) { -#ifdef TPP_MLIR - case ov::mlir::MLIR_MODE_TPP: { - tpp::DefaultPipelineOptions defPipelineOpts; - pm.addPass(tpp::createDefaultPipeline(defPipelineOpts)); - break; - } -#endif -#ifdef GRAPH_COMPILER - case ov::mlir::MLIR_MODE_GC: { - gc::GPUPipelineOptions opts; - gc::populateGPUPipeline(pm, opts); - break; - } -#endif - default: { - assert(ov::mlir::MLIR_MODE_DEFAULT); - // Cleanup before bufferization. - // Simplifies IR to allow better bufferization. - pm.addNestedPass(createCanonicalizerPass()); - pm.addNestedPass(createCSEPass()); - - // Rewrite shape ops in tensor/arith/etc - pm.addPass(createConvertShapeToStandardPass()); - - // Remove empty tensors to avoid converting them into temporary buffers. - pm.addPass(bufferization::createEmptyTensorEliminationPass()); - - pm.addPass(bufferization::createOneShotBufferizePass()); - - // Cleanup after bufferization - possibly remove redundant copies. - pm.addNestedPass(createCanonicalizerPass()); - pm.addNestedPass(createCSEPass()); - - // Deallocation pipeline to avoid memory leaks from created temporary buffers. - memref::ExpandReallocPassOptions expandReallocOpts; - expandReallocOpts.emitDeallocs = false; - pm.addPass(memref::createExpandReallocPass(expandReallocOpts)); - pm.addPass(createCanonicalizerPass()); - bufferization::OwnershipBasedBufferDeallocationPassOptions deallocOpts; - deallocOpts.privateFuncDynamicOwnership = false; - pm.addPass(bufferization::createOwnershipBasedBufferDeallocationPass(deallocOpts)); - pm.addPass(createCanonicalizerPass()); - pm.addPass(bufferization::createBufferDeallocationSimplificationPass()); - pm.addPass(bufferization::createLowerDeallocationsPass()); - pm.addPass(createCSEPass()); - pm.addPass(createCanonicalizerPass()); - - // Blanket-convert any remaining high-level vector ops to loops if any remain. - pm.addNestedPass(createConvertVectorToSCFPass()); - // pm.addNestedPass(createLinalgGeneralizeNamedOpsPass()); - // Blanket-convert any remaining linalg ops to loops if any remain. - pm.addNestedPass(createConvertLinalgToLoopsPass()); - // Blanket-convert any remaining affine ops if any remain. - pm.addPass(createLowerAffinePass()); - // Convert SCF to CF (always needed). - pm.addPass(createSCFToControlFlowPass()); - // Sprinkle some cleanups. - pm.addPass(createCanonicalizerPass()); - pm.addPass(createCSEPass()); - pm.addPass(createArithToLLVMConversionPass()); - // Blanket-convert any remaining linalg ops to LLVM if any remain. - // pm.addPass(createConvertLinalgToLLVMPass()); // no such pass - // Convert vector to LLVM (always needed). - pm.addPass(createConvertVectorToLLVMPass()); - // Convert Math to LLVM (always needed). - pm.addNestedPass(createConvertMathToLLVMPass()); - // Expand complicated MemRef operations before lowering them. - pm.addPass(memref::createExpandStridedMetadataPass()); - // The expansion may create affine expressions. Get rid of them. - pm.addPass(createLowerAffinePass()); - // Convert MemRef to LLVM (always needed). - // pm.addPass(memref::createExpandOpsPass()); - pm.addPass(createFinalizeMemRefToLLVMConversionPass()); - // Convert Func to LLVM (always needed). - pm.addPass(createConvertFuncToLLVMPass()); - // Convert Index to LLVM (always needed). - pm.addPass(createConvertIndexToLLVMPass()); - // Convert remaining unrealized_casts (always needed). - pm.addPass(createReconcileUnrealizedCastsPass()); - } - } + gc::GPUPipelineOptions opts; + gc::populateGPUPipeline(pm, opts); auto result = pm.run(module.get()); if (failed(result)) { @@ -174,73 +51,6 @@ void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module, } } -std::unique_ptr lowerToLLVMIR(Operation* module, llvm::LLVMContext& llvmContext) { - // Default lowering for mlir-cpu-runner - auto llvmModule = translateModuleToLLVMIR(module, llvmContext); - assert(llvmModule); - - // Target machine, null if not specified - std::unique_ptr targetMachine; - - std::string triple = "x86_64-linux-gnu"; - std::string cpuName = "alderlake"; // sapphirerapids, nehalem, etc. - std::string fpuName = "avx2"; // sse4.2, avx, avx2, avx512bf16, etc. - bool printLLVM = false; - auto codeGenOpt = 2; - - // Specify target machine - if (!triple.empty() && !cpuName.empty()) { - std::string error; - llvm::Triple tripleObj(triple); - const llvm::Target* target = llvm::TargetRegistry::lookupTarget(tripleObj, error); - if (!target) { - llvm::errs() << "Error while looking up target triple: "; - llvm::errs() << error << "\n"; - return nullptr; - } - - // These options should force fused MLA, but they don't. :/ - // Adding unsafe math attribute to functions below do the trick. - llvm::TargetOptions targetOptions; - // targetOptions.UnsafeFPMath = true; - targetOptions.AllowFPOpFusion = llvm::FPOpFusion::FPOpFusionMode::Fast; - auto llvmTriple = llvm::Triple(triple); - targetMachine.reset(target->createTargetMachine(llvmTriple, - cpuName, - "+" + fpuName, - targetOptions, - /* reloc model */ std::nullopt, - /* code model */ std::nullopt, - llvm::CodeGenOptLevel(codeGenOpt))); - if (!targetMachine) { - llvm::errs() << "Error while looking up target CPU: "; - llvm::errs() << cpuName << "\n"; - return nullptr; - } - } - - // Run the optimized pipeline - int sizeLevel = 0; - auto optPipeline = makeOptimizingTransformer(codeGenOpt, sizeLevel, targetMachine.get()); - if (auto err = optPipeline(llvmModule.get())) { - llvmModule->print(llvm::errs(), nullptr); - llvm::errs() << "Error while passing through the LLVM pipeline: "; - llvm::errs() << err << "\n"; - return nullptr; - } - - // MLIR doesn't lower LLVM with fast-math flags, but we need that, so we - // add for each function, to get FMAs and other goodies. - for (auto& func : llvmModule->functions()) { - func.addFnAttr("unsafe-fp-math", "true"); - } - - if (printLLVM) - llvmModule->print(llvm::outs(), nullptr); - - return llvmModule; -} - // TODO: u4/i4 types are not supported struct MemRefDescriptor { MemRefDescriptor() = default; @@ -299,25 +109,6 @@ namespace mlir { using namespace ::mlir; -std::shared_ptr MLIREvaluateBase::create(OwningOpRef module, - MlirMode mode, - std::shared_ptr loweringContext) { - switch (mode) { - #ifdef GC_USE_GPU - case MLIR_MODE_GC_GPU: - return std::make_shared(std::move(module), loweringContext); - #endif - case MLIR_MODE_TPP: - case MLIR_MODE_GC: - case MLIR_MODE_DEFAULT: - return std::make_shared(std::move(module), mode); - default: - OPENVINO_THROW("Unsupported MLIR mode"); - } -} - -#ifdef GC_USE_GPU - cl_device_id extract_device_from_context(cl_context context) { size_t devices_size; cl_int err = clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &devices_size); @@ -448,53 +239,6 @@ gc::gpu::OclContext MLIREvaluateGcGPU::build_ocl_context(const ov::EvaluationCon waitListLen, reinterpret_cast(waitList.data())); } -#endif // GC_USE_GPU - -MLIREvaluate::MLIREvaluate(OwningOpRef _module, MlirMode mode) : - module(std::move(_module)) { - - OPENVINO_MLIR_DEBUG_PRINT( - "[ DEBUG ] Source MLIR:\n" - "-----------------------------------------\n"); - OPENVINO_MLIR_DEBUG(module->dump()); - OPENVINO_MLIR_DEBUG_PRINT( - "-----------------------------------------\n"); - - prepareMLIRKernelWithoutWrapper(module, mode); - - OPENVINO_MLIR_DEBUG_PRINT( - "[ DEBUG ] Target LLVM:\n" - "-----------------------------------------\n"); - OPENVINO_MLIR_DEBUG(module->dump()); - OPENVINO_MLIR_DEBUG_PRINT( - "-----------------------------------------\n"); - - auto optPipeline = mlir::makeOptimizingTransformer(2, - /*sizeLevel=*/0, // FIXME: HARDCODED - /*targetMachine=*/nullptr); - - mlir::ExecutionEngineOptions engineOptions; - engineOptions.transformer = optPipeline; // opt level looks to be overriden in lowerToLLVMIR, but is still used - // in `create` independently - engineOptions.llvmModuleBuilder = lowerToLLVMIR; - auto maybeEngine = mlir::ExecutionEngine::create(module.get(), engineOptions); - if (maybeEngine) { - engine = std::move(maybeEngine.get()); - } else { - llvm::errs() << "failed to construct an execution engine\n"; - abort(); - } -} - -bool MLIREvaluate::invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) { - auto invocationResult = engine->invokePacked("entry", args); - if (invocationResult) { - llvm::errs() << "JIT invocation failed\n"; - return false; - } - return true; -} - MLIROp::MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types, const DimensionsMap& dimensions_map) : Op(args), engine(engine), diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.hpp b/src/common/transformations/src/transformations/mlir/mlir_op.hpp index da22f10d65a051..7d5ede5705588f 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.hpp +++ b/src/common/transformations/src/transformations/mlir/mlir_op.hpp @@ -15,10 +15,8 @@ #include "common/convert_common.hpp" -#ifdef GC_USE_GPU // GC_GPU requires IMEX support #include "gc/Utils/Error.h" #include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" -#endif namespace ov { namespace mlir { @@ -26,23 +24,11 @@ namespace mlir { using ::mlir::OwningOpRef; using ::mlir::ModuleOp; using ::mlir::ExecutionEngine; -using ::mlir::ModuleOp; - -enum MlirMode { - MLIR_MODE_TPP, - MLIR_MODE_GC, - MLIR_MODE_GC_GPU, - MLIR_MODE_DEFAULT, -}; class MLIROp; class MLIREvaluateBase { public: - static std::shared_ptr create(OwningOpRef module, - MlirMode mode, - std::shared_ptr ex_context); - virtual bool requires_packed_args() const = 0; // ::invoke() doesn't require any args preprocessing so we can pass tensors as is virtual bool invoke(const ov::TensorVector& inputs, ov::TensorVector& outputs, const ov::EvaluationContext& evaluationContext) = 0; @@ -50,8 +36,6 @@ class MLIREvaluateBase { virtual ~MLIREvaluateBase() = default; }; -#ifdef GC_USE_GPU // GC_GPU requires IMEX support - class MLIREvaluateGcGPU : public MLIREvaluateBase { std::shared_ptr module; @@ -67,21 +51,6 @@ class MLIREvaluateGcGPU : public MLIREvaluateBase { static void maybe_set_result_event(const ov::EvaluationContext& evaluationContext, gc::gpu::OclContext& ctx); }; -#endif // GC_USE_GPU - -class MLIREvaluate : public MLIREvaluateBase { - OwningOpRef module; // FIXME: needs to be kept? - std::unique_ptr engine; - -public: - - MLIREvaluate(OwningOpRef _module, MlirMode mode); - bool requires_packed_args() const override { return true; } - bool invoke(const ov::TensorVector& inputs, ov::TensorVector& outputs, const ov::EvaluationContext& evaluationContext) override { return false; }; - bool invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) override; -}; - - // Maps [output index][dimension index] -> [input index][dimension index] to infer shapes for entire subgraph using DimensionsMap = std::vector>>; diff --git a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp index 0bc05dc4fd8d60..4680d1e2a0772b 100644 --- a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp +++ b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp @@ -83,7 +83,6 @@ #include "transformations/fp16_compression/mark_floatpoint_range.hpp" #include "transformations/init_node_info.hpp" #include "transformations/op_conversions/convert_avgpool_downgrade.hpp" -#include "transformations/mlir/convert.hpp" #include "transformations/op_conversions/convert_batch_to_space.hpp" #include "transformations/op_conversions/convert_broadcast3.hpp" #include "transformations/op_conversions/convert_broadcast_to_tiles.hpp" diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index b1dce5f68dfb50..096b0260c91671 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -1723,6 +1723,7 @@ void TransformationsPipeline::apply(std::shared_ptr func) { manager.register_pass(); +#ifdef GRAPH_COMPILER auto loweringContext = std::make_shared(); auto it = m_context->get_property().find(ov::intel_gpu::ocl_context.name()); if (it != m_context->get_property().end()) { @@ -1731,6 +1732,7 @@ void TransformationsPipeline::apply(std::shared_ptr func) { loweringContext->insert(ov::intel_gpu::ocl_context(it->second.as())); } ov::pass::transformMLIR(func, loweringContext); +#endif // This is supposed to be the last pass to ensure that we don't have name collisions until // GPU plugin stops using friendly names for program creation From c2de20b4fb58c39afdb50146f2eefb2b9a52f3a5 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Tue, 14 Jul 2026 16:30:35 +0200 Subject: [PATCH 080/121] Moved converters to GPU plugin (#3) --- CMakeLists.txt | 9 --- cmake/developer_package/plugins/plugins.cmake | 9 ++- cmake/graph-compiler.cmake | 72 ++++++++----------- src/cmake/openvino.cmake | 13 ---- src/common/transformations/CMakeLists.txt | 13 +--- src/core/CMakeLists.txt | 4 +- src/plugins/intel_gpu/CMakeLists.txt | 35 ++++++++- .../include/transformations/mlir/convert.hpp | 8 +-- .../transformations/mlir/common/README.md | 0 .../mlir/common/conversion_context.cpp | 0 .../mlir/common/conversion_context.hpp | 0 .../mlir/common/convert_common.cpp | 0 .../mlir/common/convert_common.hpp | 0 .../mlir/common/converters/binary_eltwise.hpp | 0 .../mlir/common/converters/concat.hpp | 0 .../mlir/common/converters/floor.hpp | 0 .../mlir/common/converters/gather.hpp | 0 .../mlir/common/converters/matmul.hpp | 0 .../mlir/common/converters/reduce.hpp | 0 .../mlir/common/converters/relu.hpp | 0 .../mlir/common/converters/reshape.hpp | 0 .../mlir/common/converters/sdpa.hpp | 0 .../mlir/common/converters/shape_of.hpp | 0 .../mlir/common/converters/slice.hpp | 0 .../mlir/common/converters/squeeze.hpp | 0 .../mlir/common/converters/transpose.hpp | 0 .../mlir/common/converters/unary_eltwise.hpp | 0 .../mlir/common/converters/unsqueeze.hpp | 0 .../transformations/mlir/common/typedefs.hpp | 0 .../mlir/conversion/patterns.cpp | 0 .../mlir/conversion/patterns.hpp | 0 .../plugin}/transformations/mlir/convert.cpp | 15 +--- .../transformations/mlir/graph_converter.cpp | 0 .../transformations/mlir/graph_converter.hpp | 0 .../plugin}/transformations/mlir/mlir_op.cpp | 0 .../plugin}/transformations/mlir/mlir_op.hpp | 7 +- .../transformations/mlir/subgraph_tracker.cpp | 0 .../transformations/mlir/subgraph_tracker.hpp | 0 .../intel_gpu/tests/functional/CMakeLists.txt | 4 ++ .../intel_gpu/tests/unit/CMakeLists.txt | 8 +++ .../shared_test_classes/base/benchmark.hpp | 23 ++++++ 41 files changed, 116 insertions(+), 104 deletions(-) rename src/{common/transformations => plugins/intel_gpu}/include/transformations/mlir/convert.hpp (73%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/README.md (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/conversion_context.cpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/conversion_context.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/convert_common.cpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/convert_common.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/binary_eltwise.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/concat.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/floor.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/gather.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/matmul.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/reduce.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/relu.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/reshape.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/sdpa.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/shape_of.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/slice.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/squeeze.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/transpose.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/unary_eltwise.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/converters/unsqueeze.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/common/typedefs.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/conversion/patterns.cpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/conversion/patterns.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/convert.cpp (97%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/graph_converter.cpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/graph_converter.hpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/mlir_op.cpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/mlir_op.hpp (92%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/subgraph_tracker.cpp (100%) rename src/{common/transformations/src => plugins/intel_gpu/src/plugin}/transformations/mlir/subgraph_tracker.hpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 256db9bd82436e..726625a4542ab6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,15 +142,6 @@ function(ov_developer_package_export_targets) endfunction() -# -# Graph Compiler -# -if (ENABLE_GRAPH_COMPILER) - include(cmake/graph-compiler.cmake) - add_definitions(-DGRAPH_COMPILER) - add_definitions(-DGC_USE_GPU) -endif() - # # Build # diff --git a/cmake/developer_package/plugins/plugins.cmake b/cmake/developer_package/plugins/plugins.cmake index 71279b5a560cb5..8543e6c1d88085 100644 --- a/cmake/developer_package/plugins/plugins.cmake +++ b/cmake/developer_package/plugins/plugins.cmake @@ -31,6 +31,7 @@ endif() # [SOURCES ] # [OBJECT_LIBRARIES ] # [VERSION_DEFINES_FOR ] +# [LINKABLE] Build as a shared library so tests can link it # [SKIP_INSTALL] # [SKIP_REGISTRATION] Skip creation of .xml # [ADD_CLANG_FORMAT] @@ -38,7 +39,7 @@ endif() # ) # function(ov_add_plugin) - set(options SKIP_INSTALL PSEUDO_DEVICE ADD_CLANG_FORMAT ADD_CLANG_TIDY AS_EXTENSION SKIP_REGISTRATION) + set(options SKIP_INSTALL PSEUDO_DEVICE ADD_CLANG_FORMAT ADD_CLANG_TIDY AS_EXTENSION SKIP_REGISTRATION LINKABLE) set(oneValueArgs NAME DEVICE_NAME VERSION_DEFINES_FOR PSEUDO_PLUGIN_FOR) set(multiValueArgs DEFAULT_CONFIG SOURCES OBJECT_LIBRARIES) cmake_parse_arguments(OV_PLUGIN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -60,7 +61,11 @@ function(ov_add_plugin) endforeach() if(BUILD_SHARED_LIBS) - set(library_type MODULE) + if(OV_PLUGIN_LINKABLE) + set(library_type SHARED) + else() + set(library_type MODULE) + endif() else() set(library_type STATIC) endif() diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index 6143572b364e1c..36683e5c28de74 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -1,43 +1,33 @@ +include_guard() include("${CMAKE_CURRENT_LIST_DIR}/llvm.cmake") -get_property(GRAPH_COMPILER_LIBS GLOBAL PROPERTY GRAPH_COMPILER_LIBS) -if (NOT DEFINED GRAPH_COMPILER_LIBS) - if (DEFINED GraphCompiler_DIR AND EXISTS "${GraphCompiler_DIR}/GraphCompilerTargets.cmake") - include("${GraphCompiler_DIR}/GraphCompilerTargets.cmake") - elseif (DEFINED GraphCompiler_ROOT) - include("${GraphCompiler_ROOT}/lib/cmake/GraphCompiler/GraphCompilerTargets.cmake") - else() - find_package(GraphCompiler QUIET) - if (NOT GraphCompiler_FOUND) - set(GRAPH_COMPILER_REPO "https://github.com/intel-sandbox/graph-compiler" CACHE STRING "GraphCompiler repository URL") - set(GRAPH_COMPILER_TAG "main" CACHE STRING "GraphCompiler git tag/branch") - message(STATUS "GraphCompiler not found, fetching from: ${GRAPH_COMPILER_REPO}") - include(FetchContent) - FetchContent_Declare( - GraphCompiler - GIT_REPOSITORY ${GRAPH_COMPILER_REPO} - GIT_TAG ${GRAPH_COMPILER_TAG} - GIT_SHALLOW TRUE - ) - set(GC_ENABLE_TEST OFF CACHE BOOL "" FORCE) - set(GC_ENABLE_TOOLS OFF CACHE BOOL "" FORCE) - set(GC_ENABLE_PYTHON_BINDINGS OFF CACHE BOOL "" FORCE) - set(GC_DYLINK ${LLVM_DYLINK} CACHE BOOL "" FORCE) - set(_ov_build_shared_libs ${BUILD_SHARED_LIBS}) - set(BUILD_SHARED_LIBS OFF) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections") - FetchContent_MakeAvailable(GraphCompiler) - set(BUILD_SHARED_LIBS ${_ov_build_shared_libs}) - endif() - endif() - - if(LLVM_DYLINK) - set(GRAPH_COMPILER_LIBS GcInterface GraphCompiler) - else() - set(GRAPH_COMPILER_LIBS GcInterface MLIRLinalgx GcGpuOclRuntime GcGpuPasses GcGpuOclPasses) - endif() - set_property(GLOBAL PROPERTY GRAPH_COMPILER_LIBS ${GRAPH_COMPILER_LIBS}) -endif () - -get_target_property(GRAPH_COMPILER_INCLUDES GcInterface INTERFACE_INCLUDE_DIRECTORIES) -get_target_property(GRAPH_COMPILER_COMPILE_OPTIONS GcInterface INTERFACE_COMPILE_OPTIONS) +if (DEFINED GraphCompiler_DIR) + find_package(GraphCompiler REQUIRED CONFIG PATHS "${GraphCompiler_DIR}" NO_DEFAULT_PATH) +elseif (DEFINED GraphCompiler_ROOT) + find_package(GraphCompiler REQUIRED CONFIG + PATHS "${GraphCompiler_ROOT}/lib/cmake/GraphCompiler" + NO_DEFAULT_PATH) +else() + find_package(GraphCompiler QUIET CONFIG) + if (NOT GraphCompiler_FOUND) + option(GRAPH_COMPILER_DYLINK "Use dynamic linking with GraphCompiler" OFF) + set(GRAPH_COMPILER_REPO "https://github.com/intel-sandbox/graph-compiler" CACHE STRING "GraphCompiler repository URL") + set(GRAPH_COMPILER_TAG "main" CACHE STRING "GraphCompiler git tag/branch") + message(STATUS "GraphCompiler not found, fetching from: ${GRAPH_COMPILER_REPO}") + include(FetchContent) + FetchContent_Declare( + GraphCompiler + GIT_REPOSITORY ${GRAPH_COMPILER_REPO} + GIT_TAG ${GRAPH_COMPILER_TAG} + GIT_SHALLOW TRUE + ) + set(GC_ENABLE_TEST OFF CACHE BOOL "" FORCE) + set(GC_ENABLE_TOOLS OFF CACHE BOOL "" FORCE) + set(GC_ENABLE_PYTHON_BINDINGS OFF CACHE BOOL "" FORCE) + set(GC_DYLINK ${GRAPH_COMPILER_DYLINK} CACHE BOOL "" FORCE) + set(_ov_build_shared_libs ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS ${GRAPH_COMPILER_DYLINK}) + FetchContent_MakeAvailable(GraphCompiler) + set(BUILD_SHARED_LIBS ${_ov_build_shared_libs}) + endif() +endif() diff --git a/src/cmake/openvino.cmake b/src/cmake/openvino.cmake index 4fc60b414e000b..6676600872b367 100644 --- a/src/cmake/openvino.cmake +++ b/src/cmake/openvino.cmake @@ -49,28 +49,15 @@ target_include_directories(${TARGET_NAME} INTERFACE $ $) -if(ENABLE_GRAPH_COMPILER) - find_package(MLIR REQUIRED CONFIG) - - if (LLVM_DYLINK) - set(MLIR_ALL_LIBS LLVM MLIR) - endif() -endif() - target_link_libraries(${TARGET_NAME} PRIVATE openvino::reference openvino::shape_inference openvino::pugixml ${CMAKE_DL_LIBS} - ${GRAPH_COMPILER_LIBS} - ${MLIR_ALL_LIBS} Threads::Threads PUBLIC $<$,$,9.1>>:stdc++fs> $<$,$,9.0>>:c++fs>) -if(ENABLE_GRAPH_COMPILER AND NOT LLVM_DYLINK) - target_link_options(${TARGET_NAME} PRIVATE -Wl,--gc-sections) -endif() if(BUILD_SHARED_LIBS) target_link_libraries(${TARGET_NAME} PRIVATE openvino::shutdown) diff --git a/src/common/transformations/CMakeLists.txt b/src/common/transformations/CMakeLists.txt index 29cfd6f655b702..ed263c94d9ddbb 100644 --- a/src/common/transformations/CMakeLists.txt +++ b/src/common/transformations/CMakeLists.txt @@ -9,12 +9,6 @@ set(PUBLIC_HEADERS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") file(GLOB_RECURSE LIBRARY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) file(GLOB_RECURSE PUBLIC_HEADERS ${PUBLIC_HEADERS_DIR}/*.hpp) -if(NOT ENABLE_GRAPH_COMPILER) - file(GLOB_RECURSE MLIR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/transformations/mlir/*.cpp) - list(REMOVE_ITEM LIBRARY_SRC ${MLIR_SRC}) -else() - find_package(MLIR REQUIRED CONFIG) -endif() # Create named folders for the sources within the .vcproj # Empty name lists them directly under the .vcproj @@ -46,12 +40,7 @@ target_link_libraries(${TARGET_NAME}_obj PRIVATE openvino::shape_inference) target_include_directories(${TARGET_NAME}_obj PRIVATE "${PUBLIC_HEADERS_DIR}" - "${CMAKE_CURRENT_SOURCE_DIR}/src" - "${MLIR_INCLUDE_DIRS}" - "${LLVM_INCLUDE_DIRS}" - "${GRAPH_COMPILER_INCLUDES}") - -target_compile_options(${TARGET_NAME}_obj PUBLIC ${GRAPH_COMPILER_COMPILE_OPTIONS}) + "${CMAKE_CURRENT_SOURCE_DIR}/src") ov_add_clang_format_target(${TARGET_NAME}_clang FOR_TARGETS ${TARGET_NAME}_obj) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b2bce106a48ec9..3f3fd933ac7f5a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -50,10 +50,10 @@ target_include_directories(openvino_core_dev INTERFACE $ $ # HACK: to make gpu properties from 'src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp' - # available in 'transformations/.../mlir_op.cpp'. Need to figure out something better. + # available in 'GPU plugin MLIR sources'. Need to figure out something better. $ # HACK: to make mlir properties from 'src/inference/dev_api/openvino/runtime/internal_properties.hpp' - # available in 'transformations/.../mlir_op.cpp'. Need to figure out something better. + # available in 'GPU plugin MLIR sources'. Need to figure out something better. $) target_include_directories(openvino_core_dev SYSTEM INTERFACE diff --git a/src/plugins/intel_gpu/CMakeLists.txt b/src/plugins/intel_gpu/CMakeLists.txt index f8d9137db5732f..fec050b248b0ce 100644 --- a/src/plugins/intel_gpu/CMakeLists.txt +++ b/src/plugins/intel_gpu/CMakeLists.txt @@ -134,9 +134,31 @@ add_subdirectory(src/graph) file(GLOB_RECURSE PLUGIN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/plugin/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/include/intel_gpu/plugin/*.hpp) +if(ENABLE_GRAPH_COMPILER) + include(${CMAKE_SOURCE_DIR}/cmake/graph-compiler.cmake) + + set(OV_GPU_MLIR_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/plugin/transformations/mlir) + file(GLOB_RECURSE GPU_MLIR_SOURCES ${OV_GPU_MLIR_DIR}/*.cpp) + list(REMOVE_ITEM PLUGIN_SOURCES ${GPU_MLIR_SOURCES}) + set(mlir_lib openvino_intel_gpu_mlir_obj) + add_library(${mlir_lib} OBJECT ${GPU_MLIR_SOURCES}) + target_compile_options(${mlir_lib} PRIVATE -Wno-error) + target_compile_definitions(${mlir_lib} PRIVATE GRAPH_COMPILER IMPLEMENT_OPENVINO_API) + target_link_libraries(${mlir_lib} PRIVATE openvino::runtime openvino_intel_gpu_graph GraphCompiler) + target_include_directories(${mlir_lib} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include/ + ${OpenVINO_SOURCE_DIR}/src/common/transformations/include + ${OV_GPU_MLIR_DIR}) + + set(GPU_PLUGIN_OBJECT_LIBRARIES ${mlir_lib}) + set(GPU_PLUGIN_OPTIONS LINKABLE) +endif() + ov_add_plugin(NAME ${TARGET_NAME} DEVICE_NAME "GPU" SOURCES ${PLUGIN_SOURCES} + OBJECT_LIBRARIES ${GPU_PLUGIN_OBJECT_LIBRARIES} + ${GPU_PLUGIN_OPTIONS} DEFAULT_CONFIG ${PLUGIN_DEFAULT_CONFIG} VERSION_DEFINES_FOR src/plugin/plugin.cpp) @@ -144,11 +166,18 @@ target_compile_options(${TARGET_NAME} PRIVATE $<$:$,/Os,-Os>>) target_link_libraries( - ${TARGET_NAME} PRIVATE openvino_intel_gpu_graph openvino::pugixml ${GRAPH_COMPILER_LIBS}) + ${TARGET_NAME} PRIVATE openvino_intel_gpu_graph openvino::pugixml) target_include_directories(${TARGET_NAME} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/include/ - "${GRAPH_COMPILER_INCLUDES}") + ${CMAKE_CURRENT_SOURCE_DIR}/include/) + +if(ENABLE_GRAPH_COMPILER) + target_link_libraries(${TARGET_NAME} PRIVATE GraphCompiler) + target_compile_definitions(${TARGET_NAME} PUBLIC GRAPH_COMPILER) + target_include_directories(${TARGET_NAME} PRIVATE + ${OpenVINO_SOURCE_DIR}/src/common/transformations/include + ${OV_GPU_MLIR_DIR}) +endif() ov_set_threading_interface_for(${TARGET_NAME}) ov_gpu_set_runtime_interface_for(${TARGET_NAME}) diff --git a/src/common/transformations/include/transformations/mlir/convert.hpp b/src/plugins/intel_gpu/include/transformations/mlir/convert.hpp similarity index 73% rename from src/common/transformations/include/transformations/mlir/convert.hpp rename to src/plugins/intel_gpu/include/transformations/mlir/convert.hpp index f3dfaf9f0337c5..5c40b766ffdb68 100644 --- a/src/common/transformations/include/transformations/mlir/convert.hpp +++ b/src/plugins/intel_gpu/include/transformations/mlir/convert.hpp @@ -4,10 +4,10 @@ #pragma once +#include "openvino/core/core_visibility.hpp" #include "openvino/core/model.hpp" #include "openvino/core/partial_shape.hpp" #include "openvino/util/env_util.hpp" -#include "transformations_visibility.hpp" namespace ov { @@ -17,8 +17,8 @@ inline bool is_mlir_transform_enabled() { return util::getenv_bool("OV_MLIR", false); } -void TRANSFORMATIONS_API transformMLIR(std::shared_ptr model, - std::shared_ptr loweringContext); +OPENVINO_API void transformMLIR(std::shared_ptr model, + std::shared_ptr loweringContext); } @@ -28,7 +28,7 @@ namespace mlir { // the op's dimensions_map. Asserts if the node is not an MLIROp. // Exposed here so callers outside transformations (e.g. Intel GPU plugin) can // invoke shape inference without depending on the private MLIROp header. -std::vector TRANSFORMATIONS_API mlir_op_shape_infer( +OPENVINO_API std::vector mlir_op_shape_infer( const std::shared_ptr& op, const std::vector& input_shapes); diff --git a/src/common/transformations/src/transformations/mlir/common/README.md b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/README.md similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/README.md rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/README.md diff --git a/src/common/transformations/src/transformations/mlir/common/conversion_context.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.cpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/conversion_context.cpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.cpp diff --git a/src/common/transformations/src/transformations/mlir/common/conversion_context.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/conversion_context.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/convert_common.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/convert_common.cpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp diff --git a/src/common/transformations/src/transformations/mlir/common/convert_common.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/convert_common.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/binary_eltwise.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/binary_eltwise.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/concat.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/concat.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/floor.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/floor.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/floor.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/floor.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/gather.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/gather.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/matmul.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/matmul.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/reduce.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/reduce.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/relu.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/relu.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/reshape.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/reshape.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/sdpa.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/sdpa.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/sdpa.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/sdpa.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/shape_of.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/shape_of.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/shape_of.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/shape_of.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/slice.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/slice.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/squeeze.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/squeeze.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/transpose.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/transpose.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/transpose.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/transpose.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/unary_eltwise.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unary_eltwise.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/unary_eltwise.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unary_eltwise.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/converters/unsqueeze.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unsqueeze.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/converters/unsqueeze.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unsqueeze.hpp diff --git a/src/common/transformations/src/transformations/mlir/common/typedefs.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/typedefs.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/common/typedefs.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/common/typedefs.hpp diff --git a/src/common/transformations/src/transformations/mlir/conversion/patterns.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/conversion/patterns.cpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp diff --git a/src/common/transformations/src/transformations/mlir/conversion/patterns.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/conversion/patterns.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp diff --git a/src/common/transformations/src/transformations/mlir/convert.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp similarity index 97% rename from src/common/transformations/src/transformations/mlir/convert.cpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp index f57db096bee9b1..b955dbcf45b126 100644 --- a/src/common/transformations/src/transformations/mlir/convert.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp @@ -84,7 +84,6 @@ #include "openvino/pass/pattern/op/wrap_type.hpp" #include "subgraph_tracker.hpp" #include "transformations/symbolic_transformations/symbolic_optimizations.hpp" -#include "transformations_visibility.hpp" namespace { @@ -392,20 +391,12 @@ void injectMLIR(std::shared_ptr model, model->validate_nodes_and_infer_types(); } -void loadDialects(MLIRContext* context) { - context->loadAllAvailableDialects(); -} - MLIRContext* get_shared_mlir_context() { - // Gives MLIRContext instance shared for entire OV process and initialized once upon the initial request - // FIXME: Bind with OpenVINO lifetime in the sutable class instead of dirty tricking with static lifetime - - static std::shared_ptr context = [] { - auto ctx = std::make_shared(gc::getDialectRegistry()); - loadDialects(ctx.get()); + static auto context = [] { + auto ctx = std::make_unique(gc::getDialectRegistry()); + ctx->loadAllAvailableDialects(); return ctx; }(); - return context.get(); } diff --git a/src/common/transformations/src/transformations/mlir/graph_converter.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/graph_converter.cpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp diff --git a/src/common/transformations/src/transformations/mlir/graph_converter.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/graph_converter.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/mlir_op.cpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp diff --git a/src/common/transformations/src/transformations/mlir/mlir_op.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.hpp similarity index 92% rename from src/common/transformations/src/transformations/mlir/mlir_op.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.hpp index 7d5ede5705588f..46ebd065c0e1b4 100644 --- a/src/common/transformations/src/transformations/mlir/mlir_op.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.hpp @@ -4,18 +4,13 @@ #pragma once -#include "mlir/IR/OwningOpRef.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/ExecutionEngine/ExecutionEngine.h" -#include "mlir/ExecutionEngine/JitRunner.h" -#include "mlir/ExecutionEngine/OptUtils.h" #include "openvino/op/op.hpp" -#include "openvino/core/shape.hpp" #include "common/convert_common.hpp" -#include "gc/Utils/Error.h" #include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" namespace ov { @@ -55,7 +50,7 @@ class MLIREvaluateGcGPU : public MLIREvaluateBase { using DimensionsMap = std::vector>>; -class OPENVINO_API MLIROp : public ov::op::Op { +class MLIROp : public ov::op::Op { std::shared_ptr engine; OVOutputTypes output_types; DimensionsMap dimensions_map; diff --git a/src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/subgraph_tracker.cpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp diff --git a/src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp similarity index 100% rename from src/common/transformations/src/transformations/mlir/subgraph_tracker.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp diff --git a/src/plugins/intel_gpu/tests/functional/CMakeLists.txt b/src/plugins/intel_gpu/tests/functional/CMakeLists.txt index c8234ed3d956a5..379d7bc7f04e58 100644 --- a/src/plugins/intel_gpu/tests/functional/CMakeLists.txt +++ b/src/plugins/intel_gpu/tests/functional/CMakeLists.txt @@ -39,6 +39,10 @@ ov_add_test_target( OV GPU ) +if(ENABLE_GRAPH_COMPILER) + target_compile_definitions(${TARGET_NAME} PRIVATE GRAPH_COMPILER) +endif() + ov_gpu_set_runtime_interface_for(${TARGET_NAME}) if(ENABLE_PROXY) diff --git a/src/plugins/intel_gpu/tests/unit/CMakeLists.txt b/src/plugins/intel_gpu/tests/unit/CMakeLists.txt index baf5d8a1c6ec21..c2ee209625e9f9 100644 --- a/src/plugins/intel_gpu/tests/unit/CMakeLists.txt +++ b/src/plugins/intel_gpu/tests/unit/CMakeLists.txt @@ -34,6 +34,10 @@ file(GLOB_RECURSE SOURCES_MAIN "${CMAKE_HOME_DIRECTORY}/src/plugins/intel_gpu/src/plugin/simple_math.cpp" ) +if(TARGET openvino_intel_gpu_plugin) + list(REMOVE_ITEM SOURCES_MAIN ${GPU_MLIR_SOURCES}) +endif() + # Those tests have dependency on OpenCL runtime # Need to be excluded from the build with a different runtime file(GLOB_RECURSE SOURCES_WITH_OCL_RT @@ -115,6 +119,10 @@ target_link_libraries(${TARGET_NAME} PRIVATE openvino_intel_gpu_graph openvino::reference gmock) +if(ENABLE_GRAPH_COMPILER) + target_link_libraries(${TARGET_NAME} PRIVATE openvino_intel_gpu_plugin) +endif() + target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/test_utils/ $ diff --git a/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp b/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp index 764b8a4d03a52c..fe112e40548641 100644 --- a/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp +++ b/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp @@ -146,7 +146,13 @@ class BenchmarkLayerTest : public BaseLayerTest { // Benchmark for (int i = 0; i < num_attempts_; ++i) { +#ifdef GRAPH_COMPILER + const auto start = std::chrono::steady_clock::now(); this->inferRequest.infer(); + const auto end = std::chrono::steady_clock::now(); +#else + this->inferRequest.infer(); +#endif const auto& profiling_info = this->inferRequest.get_profiling_info(); for (auto& res : results_us) { const std::string node_type_name = res.first; @@ -155,6 +161,23 @@ class BenchmarkLayerTest : public BaseLayerTest { [&node_type_name](const ProfilingInfo& profile) { return profile.node_type == node_type_name; }); +#ifdef GRAPH_COMPILER + if (node_type_name == "MLIROp") { + uint64_t profile_time = 0; + for (const auto& profile : profiling_info) { + if (profile.node_type == "Parameter" || profile.node_type == "Result") { + continue; + } + profile_time += profile.real_time.count(); + } + + if (profile_time) + time += profile_time; + else + time += std::chrono::duration_cast(end - start).count(); + continue; + } +#endif if (found_profile == profiling_info.end()) { OPENVINO_THROW("Cannot find operator by node type: ", node_type_name); } From f322e7c8c38da3b2b600b831aebe4bf17bfcd04f Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Tue, 14 Jul 2026 17:13:22 +0200 Subject: [PATCH 081/121] Graph Compiler CI (#4) --- .github/workflows/graph-compiler.yml | 74 ++++++++++++++++++++++++++++ cmake/graph-compiler.cmake | 51 +++++++++---------- cmake/llvm.cmake | 37 +++++--------- install_build_dependencies.sh | 2 +- 4 files changed, 109 insertions(+), 55 deletions(-) create mode 100644 .github/workflows/graph-compiler.yml diff --git a/.github/workflows/graph-compiler.yml b/.github/workflows/graph-compiler.yml new file mode 100644 index 00000000000000..268c89545785b8 --- /dev/null +++ b/.github/workflows/graph-compiler.yml @@ -0,0 +1,74 @@ +name: Graph Compiler GPU Backend + +on: + push: + pull_request: + +permissions: + contents: read + +env: + GC_REPO: https://x-access-token:${{ secrets.GC_TOKEN }}@github.com/intel-sandbox/graph-compiler + GC_TAG: main + BUILD_DIR: ${{ github.workspace }}-gc-build + OUTPUT_DIR: ${{ github.workspace }}-gc-bin + +jobs: + ci: + runs-on: ${{ vars.RUNNER }} + + steps: + - uses: actions/checkout@v7 + with: + submodules: recursive + + - name: Build + run: | + mkdir -p $BUILD_DIR + run_build() { + [ "$1" = '-c' ] && rm -rf $BUILD_DIR + mkdir -p $BUILD_DIR + cmake -G Ninja -S "$GITHUB_WORKSPACE" -B "$BUILD_DIR" \ + -DOUTPUT_ROOT="$OUTPUT_DIR" \ + -DGRAPH_COMPILER_REPO="$GC_REPO" \ + -DGRAPH_COMPILER_TAG="$GC_TAG" \ + -DENABLE_INTEL_GPU=ON \ + -DENABLE_GRAPH_COMPILER=ON \ + -DENABLE_TESTS=ON \ + -DENABLE_ONEDNN_FOR_GPU=OFF \ + -DENABLE_INTEL_CPU=OFF \ + -DENABLE_INTEL_NPU=OFF \ + -DCMAKE_CXX_FLAGS="-DOV_GPU_OPENCL_HPP_HAS_UUID -DOV_GPU_OPENCL_HPP_HAS_BUS_INFO" \ + -DOpenCL_HPP_INCLUDE_DIR="$GITHUB_WORKSPACE/thirdparty/ocl/clhpp_headers/include" \ + -DOpenCL_HPP="$GITHUB_WORKSPACE/thirdparty/ocl/clhpp_headers/include/CL/opencl.hpp" + cmake --build "$BUILD_DIR" --parallel + } + + if [ -d "$BUILD_DIR" ]; then + # Reuse build artifacts from previous build. If the build fails, clean and rebuild. + run_build || run_build -c + else + run_build + fi + + - name: Test + run: | + # These tests require MLIR patches: + # https://github.com/llvm/llvm-project/pull/208932 + # https://github.com/llvm/llvm-project/pull/197281 + exclude='.*ScaledAttnLayerGPUMlirTest.CompareWithRefs.*|mlir_Transpose.*|mlir_ReshapeAndTranspose.*' + + export OV_MLIR=1 + func_tests="$OUTPUT_DIR/bin/intel64/Release/ov_gpu_func_tests" + filter=$(printf '%s:' \ + 'MLIRExecution.SimpleMatmulf16' \ + 'MLIRExecution.SDPABasic' \ + '*ScaledAttnLayerGPUMlirTest*' \ + 'mlir_*' \ + ) + tests=$("$func_tests" --gtest_list_tests --gtest_filter="${filter%:}" \ + | awk -v exclude="$exclude" '/^ /{print suite $1} /^[^ ]/{suite=$1}' \ + | grep -v -E "$exclude") + start=$SECONDS + echo "$tests" | xargs -P 32 -I{} "$func_tests" --gtest_filter='{}' + echo "Run $(echo "$tests" | wc -l) tests in $((SECONDS - start))s" diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index 36683e5c28de74..c1bd561c1df87b 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -1,33 +1,26 @@ include_guard() include("${CMAKE_CURRENT_LIST_DIR}/llvm.cmake") -if (DEFINED GraphCompiler_DIR) - find_package(GraphCompiler REQUIRED CONFIG PATHS "${GraphCompiler_DIR}" NO_DEFAULT_PATH) -elseif (DEFINED GraphCompiler_ROOT) - find_package(GraphCompiler REQUIRED CONFIG - PATHS "${GraphCompiler_ROOT}/lib/cmake/GraphCompiler" - NO_DEFAULT_PATH) -else() - find_package(GraphCompiler QUIET CONFIG) - if (NOT GraphCompiler_FOUND) - option(GRAPH_COMPILER_DYLINK "Use dynamic linking with GraphCompiler" OFF) - set(GRAPH_COMPILER_REPO "https://github.com/intel-sandbox/graph-compiler" CACHE STRING "GraphCompiler repository URL") - set(GRAPH_COMPILER_TAG "main" CACHE STRING "GraphCompiler git tag/branch") - message(STATUS "GraphCompiler not found, fetching from: ${GRAPH_COMPILER_REPO}") - include(FetchContent) - FetchContent_Declare( - GraphCompiler - GIT_REPOSITORY ${GRAPH_COMPILER_REPO} - GIT_TAG ${GRAPH_COMPILER_TAG} - GIT_SHALLOW TRUE - ) - set(GC_ENABLE_TEST OFF CACHE BOOL "" FORCE) - set(GC_ENABLE_TOOLS OFF CACHE BOOL "" FORCE) - set(GC_ENABLE_PYTHON_BINDINGS OFF CACHE BOOL "" FORCE) - set(GC_DYLINK ${GRAPH_COMPILER_DYLINK} CACHE BOOL "" FORCE) - set(_ov_build_shared_libs ${BUILD_SHARED_LIBS}) - set(BUILD_SHARED_LIBS ${GRAPH_COMPILER_DYLINK}) - FetchContent_MakeAvailable(GraphCompiler) - set(BUILD_SHARED_LIBS ${_ov_build_shared_libs}) - endif() +find_package(GraphCompiler QUIET CONFIG) + +if (NOT GraphCompiler_FOUND) + option(GRAPH_COMPILER_DYLINK "Use dynamic linking with GraphCompiler" OFF) + set(GRAPH_COMPILER_REPO "https://github.com/intel-sandbox/graph-compiler" CACHE STRING "GraphCompiler repository URL") + set(GRAPH_COMPILER_TAG "main" CACHE STRING "GraphCompiler git tag/branch") + message(STATUS "GraphCompiler not found, fetching from: ${GRAPH_COMPILER_REPO}") + include(FetchContent) + FetchContent_Declare( + GraphCompiler + GIT_REPOSITORY ${GRAPH_COMPILER_REPO} + GIT_TAG ${GRAPH_COMPILER_TAG} + GIT_SHALLOW TRUE + ) + set(GC_ENABLE_TEST OFF CACHE BOOL "" FORCE) + set(GC_ENABLE_TOOLS OFF CACHE BOOL "" FORCE) + set(GC_ENABLE_PYTHON_BINDINGS OFF CACHE BOOL "" FORCE) + set(GC_DYLINK ${GRAPH_COMPILER_DYLINK} CACHE BOOL "" FORCE) + set(_ov_build_shared_libs ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS ${GRAPH_COMPILER_DYLINK}) + FetchContent_MakeAvailable(GraphCompiler) + set(BUILD_SHARED_LIBS ${_ov_build_shared_libs}) endif() diff --git a/cmake/llvm.cmake b/cmake/llvm.cmake index cabcd7778ea485..4655dac5073a58 100644 --- a/cmake/llvm.cmake +++ b/cmake/llvm.cmake @@ -1,30 +1,17 @@ include_guard() -find_package(LLVM CONFIG QUIET) -find_package(MLIR CONFIG QUIET) +set(SUPPORTED_LLVM_VERSION "23" CACHE STRING "") -if (NOT LLVM_FOUND OR NOT MLIR_FOUND) - # Try apt.llvm.org install paths explicitly - set(LLVM_VERSION "23" CACHE STRING "LLVM nightly major version from apt.llvm.org") - set(llvm_apt_dir "/usr/lib/llvm-${LLVM_VERSION}/lib/cmake/llvm") - set(mlir_apt_dir "/usr/lib/llvm-${LLVM_VERSION}/lib/cmake/mlir") - - if (EXISTS "${llvm_apt_dir}/LLVMConfig.cmake" AND EXISTS "${mlir_apt_dir}/MLIRConfig.cmake") - set(LLVM_DIR "${llvm_apt_dir}" CACHE PATH "" FORCE) - set(MLIR_DIR "${mlir_apt_dir}" CACHE PATH "" FORCE) - else() - message(FATAL_ERROR - "LLVM/MLIR not found. Either install LLVM nightly:\n" - " sudo ./install_build_dependencies.sh -llvm\n" - "Or add the following CMake options:\n" - " -DLLVM_DIR=path/to/llvm/lib/cmake/llvm\n" - " -DMLIR_DIR=path/to/llvm/lib/cmake/mlir\n" - ) - endif() +find_package(LLVM CONFIG QUIET) +if (NOT LLVM_FOUND OR NOT LLVM_VERSION_MAJOR EQUAL ${SUPPORTED_LLVM_VERSION}) + set(LLVM_DIR "/usr/lib/llvm-${SUPPORTED_LLVM_VERSION}/lib/cmake/llvm" CACHE PATH "" FORCE) + find_package(LLVM REQUIRED CONFIG) endif() -find_package(LLVM REQUIRED CONFIG) -find_package(MLIR REQUIRED CONFIG) - -message(STATUS "LLVM ${LLVM_PACKAGE_VERSION} at ${LLVM_DIR}") -message(STATUS "MLIR at ${MLIR_DIR}") +find_package(MLIR CONFIG QUIET) +if (NOT MLIR_FOUND) + get_filename_component(llvm_cmake_path "${LLVM_DIR}" REALPATH) + get_filename_component(llvm_cmake_dir "${llvm_cmake_path}" DIRECTORY) + set(MLIR_DIR "${llvm_cmake_dir}/mlir" CACHE PATH "" FORCE) + find_package(MLIR REQUIRED CONFIG) +endif() diff --git a/install_build_dependencies.sh b/install_build_dependencies.sh index cf881ce55c905e..9744ddd33f3473 100755 --- a/install_build_dependencies.sh +++ b/install_build_dependencies.sh @@ -94,7 +94,7 @@ if [ -f /etc/lsb-release ] || [ -f /etc/debian_version ] ; then # LLVM/MLIR nightly from apt.llvm.org for arg in "$@"; do if [ "$arg" = "-llvm" ]; then - LLVM_VERSION=$(grep -Po '(?<=set\(LLVM_VERSION ")[^"]*' "$(dirname "$0")/cmake/llvm.cmake") + : ${LLVM_VERSION:=$(grep -Po '(?<=set\(SUPPORTED_LLVM_VERSION ")[^"]*' "$(dirname "$0")/cmake/llvm.cmake")} if ! dpkg -l "libmlir-${LLVM_VERSION}-dev" &>/dev/null; then wget -qO- https://apt.llvm.org/llvm.sh | bash -s -- "${LLVM_VERSION}" all apt-get install -y --no-install-recommends \ From 32c9ee5713ea4a011e4aac1ede235187bcc1ca99 Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Sun, 19 Jul 2026 18:07:38 +0200 Subject: [PATCH 082/121] Replace 'generic_primitive' with 'mlir_primitive' (#6) * Replace 'generic_primitive' with 'mlir_primitive' Signed-off-by: dchigarev * Remove unnecessary cmake definitions Signed-off-by: dchigarev * revert unrelated changes Signed-off-by: dchigarev --------- Signed-off-by: dchigarev --- src/plugins/intel_gpu/CMakeLists.txt | 4 + .../intel_gpu/plugin/primitives_list.hpp | 3 + .../intel_gpu/plugin/program_builder.hpp | 2 - .../primitives/generic_primitive.hpp | 45 ----- .../intel_gpu/primitives/mlir_primitive.hpp | 44 +++++ .../intel_gpu/src/graph/generic_primitive.cpp | 57 ------ .../graph/impls/common/generic_primitive.cpp | 86 --------- .../graph/impls/common/generic_primitive.hpp | 36 ---- .../src/graph/impls/common/mlir_primitive.cpp | 167 ++++++++++++++++++ .../src/graph/impls/common/mlir_primitive.hpp | 23 +++ .../src/graph/impls/common/register.cpp | 2 +- .../src/graph/impls/common/register.hpp | 2 +- .../graph/include/generic_primitive_inst.h | 44 ----- .../src/graph/include/mlir_primitive_inst.h | 46 +++++ .../intel_gpu/src/graph/mlir_primitive.cpp | 67 +++++++ ...ive_impls.cpp => mlir_primitive_impls.cpp} | 14 +- .../intel_gpu/src/graph/registry/registry.hpp | 2 +- .../intel_gpu/src/plugin/ops/generic.cpp | 73 -------- .../intel_gpu/src/plugin/ops/mlir_op.cpp | 164 ++++------------- .../intel_gpu/src/plugin/program_builder.cpp | 10 +- 20 files changed, 399 insertions(+), 492 deletions(-) delete mode 100644 src/plugins/intel_gpu/include/intel_gpu/primitives/generic_primitive.hpp create mode 100644 src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp delete mode 100644 src/plugins/intel_gpu/src/graph/generic_primitive.cpp delete mode 100644 src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp delete mode 100644 src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.hpp create mode 100644 src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp create mode 100644 src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.hpp delete mode 100644 src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h create mode 100644 src/plugins/intel_gpu/src/graph/include/mlir_primitive_inst.h create mode 100644 src/plugins/intel_gpu/src/graph/mlir_primitive.cpp rename src/plugins/intel_gpu/src/graph/registry/{generic_primitive_impls.cpp => mlir_primitive_impls.cpp} (52%) delete mode 100644 src/plugins/intel_gpu/src/plugin/ops/generic.cpp diff --git a/src/plugins/intel_gpu/CMakeLists.txt b/src/plugins/intel_gpu/CMakeLists.txt index fec050b248b0ce..43b13a6d443a13 100644 --- a/src/plugins/intel_gpu/CMakeLists.txt +++ b/src/plugins/intel_gpu/CMakeLists.txt @@ -147,6 +147,8 @@ if(ENABLE_GRAPH_COMPILER) target_link_libraries(${mlir_lib} PRIVATE openvino::runtime openvino_intel_gpu_graph GraphCompiler) target_include_directories(${mlir_lib} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/ + # FIXME: remove when ov::MLIROp definition is moved to a common plugin header + ${CMAKE_CURRENT_SOURCE_DIR}/src/plugin ${OpenVINO_SOURCE_DIR}/src/common/transformations/include ${OV_GPU_MLIR_DIR}) @@ -175,6 +177,8 @@ if(ENABLE_GRAPH_COMPILER) target_link_libraries(${TARGET_NAME} PRIVATE GraphCompiler) target_compile_definitions(${TARGET_NAME} PUBLIC GRAPH_COMPILER) target_include_directories(${TARGET_NAME} PRIVATE + # FIXME: remove when ov::MLIROp definition is moved to a common plugin header + ${CMAKE_CURRENT_SOURCE_DIR}/src/plugin ${OpenVINO_SOURCE_DIR}/src/common/transformations/include ${OV_GPU_MLIR_DIR}) endif() diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/primitives_list.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/primitives_list.hpp index d7585f3af31f4c..ca6aad03ae64e3 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/primitives_list.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/primitives_list.hpp @@ -325,3 +325,6 @@ REGISTER_FACTORY(internal, GatedDeltaNet); REGISTER_FACTORY(internal, PagedCausalConv1D); REGISTER_FACTORY(internal, GatherMatmulCompressed); REGISTER_FACTORY(internal, Atan2); +#ifdef GRAPH_COMPILER +REGISTER_FACTORY(internal, MLIR); +#endif diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/program_builder.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/program_builder.hpp index d37f5d534072a5..02848734d88830 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/program_builder.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/program_builder.hpp @@ -174,8 +174,6 @@ class ProgramBuilder final { }; void CreateCustomOp(ProgramBuilder& p, const std::shared_ptr& node, CustomLayerPtr customLayer); -void CreateMLIRSubgraphOp(ProgramBuilder& p, const std::shared_ptr& node); -void CreateGenericOp(ProgramBuilder& p, const std::shared_ptr& node); void CreateUnaryEltwiseOp(ProgramBuilder& p, const std::shared_ptr& node, cldnn::activation_func func, cldnn::activation_additional_params params); void CreateElementwiseOp(ProgramBuilder& p, diff --git a/src/plugins/intel_gpu/include/intel_gpu/primitives/generic_primitive.hpp b/src/plugins/intel_gpu/include/intel_gpu/primitives/generic_primitive.hpp deleted file mode 100644 index df01db8d3f65cb..00000000000000 --- a/src/plugins/intel_gpu/include/intel_gpu/primitives/generic_primitive.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -/////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once -#include "intel_gpu/runtime/layout.hpp" -#include "openvino/core/partial_shape.hpp" -#include "primitive.hpp" -#include "intel_gpu/runtime/memory.hpp" -#include "intel_gpu/runtime/stream.hpp" -#include -#include - -namespace cldnn { - -struct generic_primitive : public primitive_base { - CLDNN_DECLARE_PRIMITIVE(generic_primitive) - - typedef std::function& dependent_events, - cldnn::stream& stream, - const std::vector& inputs, - const std::vector& outputs)> - execute_function; - - typedef std::function(const std::vector& input_shapes)> - shape_infer_function; - - generic_primitive() : primitive_base("", {}) {} - - generic_primitive(const primitive_id& id, - const std::vector& inputs, - const execute_function& execute_f, - const shape_infer_function& shape_infer_f, - size_t num_outputs, - const std::vector& out_types) - : primitive_base(id, {inputs}, num_outputs, out_types), - execute_f(execute_f), - shape_infer_f(shape_infer_f) {} - - const execute_function execute_f; - const shape_infer_function shape_infer_f; -}; - -} // namespace cldnn diff --git a/src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp b/src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp new file mode 100644 index 00000000000000..8ffdf78d21e9ce --- /dev/null +++ b/src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp @@ -0,0 +1,44 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include + +#include "openvino/core/partial_shape.hpp" +#include "primitive.hpp" + +namespace ov { +class Node; // forward-decl — the underlying op is ov::mlir::MLIROp +} + +namespace cldnn { + +/// @brief Primitive that wraps an ov::mlir::MLIROp node. Its execute_impl +/// (see impls/common/mlir_primitive.cpp) forwards to MLIROp::evaluate(). +struct mlir_primitive : public primitive_base { + CLDNN_DECLARE_PRIMITIVE(mlir_primitive) + + using shape_infer_function = + std::function(const std::vector&)>; + + mlir_primitive() : primitive_base("", {}) {} + + mlir_primitive(const primitive_id& id, + const std::vector& inputs, + std::shared_ptr op, + shape_infer_function shape_infer_f, + size_t num_outputs, + const std::vector& out_types) + : primitive_base(id, inputs, num_outputs, out_types), + op(std::move(op)), + shape_infer_f(std::move(shape_infer_f)) {} + + std::shared_ptr op; + shape_infer_function shape_infer_f; +}; + +} // namespace cldnn diff --git a/src/plugins/intel_gpu/src/graph/generic_primitive.cpp b/src/plugins/intel_gpu/src/graph/generic_primitive.cpp deleted file mode 100644 index 883c0568aaf0e5..00000000000000 --- a/src/plugins/intel_gpu/src/graph/generic_primitive.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2018-2021 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include "generic_primitive_inst.h" -#include "openvino/core/partial_shape.hpp" -#include "primitive_type_base.h" -#include -#include "json_object.h" -#include - -namespace cldnn { - -primitive_type_id generic_primitive::type_id() { - static primitive_type_base instance; - return &instance; -} - -layout generic_primitive_inst::calc_output_layout(const generic_primitive_node& node, const kernel_impl_params& impl_param) { - return calc_output_layouts(node, impl_param)[0]; -} - -template -std::vector generic_primitive_inst::calc_output_layouts(generic_primitive_node const& /*node*/, const kernel_impl_params& impl_param) { - auto prim = impl_param.typed_desc(); - - std::vector input_shapes; - for (const auto& l : impl_param.input_layouts) { - input_shapes.push_back(l.get()); - } - - std::vector output_shapes = prim->shape_infer_f(input_shapes); - - std::vector out_layouts; - for (size_t i = 0; i < output_shapes.size(); i++) { - out_layouts.emplace_back(output_shapes[i], prim->get_output_data_type(i).value(), format::get_default_format(output_shapes[i].size())); - } - - return out_layouts; -} - -std::string generic_primitive_inst::to_string(generic_primitive_node const& node) { - auto desc = node.get_primitive(); - auto node_info = node.desc_to_json(); - - std::stringstream primitive_description; - - json_composite generic_prim_info; - node_info->add("custom primitive info", generic_prim_info); - node_info->dump(primitive_description); - - return primitive_description.str(); -} - -generic_primitive_inst::typed_primitive_inst(network& network, generic_primitive_node const& node) : parent(network, node), node(&node) {} - -} // namespace cldnn diff --git a/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp b/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp deleted file mode 100644 index 271b6548594b0b..00000000000000 --- a/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include "generic_primitive_inst.h" -#include "generic_primitive.hpp" -#include "registry/implementation_map.hpp" -#include "register.hpp" - -#include - -namespace cldnn { -namespace common { - -struct generic_primitive_impl : typed_primitive_impl { - using parent = typed_primitive_impl; - using parent::parent; - - DECLARE_OBJECT_TYPE_SERIALIZATION(cldnn::common::generic_primitive_impl) - - std::unique_ptr clone() const override { - return std::make_unique(*this); - } - - generic_primitive_impl() : parent() {} - - explicit generic_primitive_impl(const generic_primitive_node& outer) { - set_node_params(outer); - } - - void set_node_params(const program_node& arg) override { - } - - event::ptr execute_impl(const std::vector& events, generic_primitive_inst& instance) override { - std::vector inputs; - inputs.reserve(instance.inputs_memory_count()); - for (size_t i = 0; i < instance.inputs_memory_count(); i++) { - inputs.push_back(instance.input_memory_ptr(i)); - } - - std::vector outputs; - outputs.reserve(instance.outputs_memory_count()); - for (size_t i = 0; i < instance.outputs_memory_count(); i++) { - outputs.push_back(instance.output_memory_ptr(i)); - } - - return instance.node->get_primitive()->execute_f(events, instance.get_network().get_stream(), inputs, outputs); - } - - static std::unique_ptr create(const generic_primitive_node& arg, const kernel_impl_params&) { - return std::make_unique(arg); - } - - void init_kernels(const kernels_cache& , const kernel_impl_params&) override {} - - void save(BinaryOutputBuffer& ob) const override { - parent::save(ob); - } - - void load(BinaryInputBuffer& ib) override { - parent::load(ib); - } -}; - -std::unique_ptr GenericPrimitiveImplementationManager::create_impl(const program_node& node, const kernel_impl_params& params) const { - assert(node.is_type()); - return generic_primitive_impl::create(static_cast(node), params); -} - -namespace detail { - -attach_generic_primitive_common::attach_generic_primitive_common() { - implementation_map::add(impl_types::common, - shape_types::dynamic_shape, - generic_primitive_impl::create, - {}, - {}); - implementation_map::add(impl_types::common, generic_primitive_impl::create, {}); -} - -} // namespace detail -} // namespace common -} // namespace cldnn - -BIND_BINARY_BUFFER_WITH_TYPE(cldnn::common::generic_primitive_impl) -BIND_BINARY_BUFFER_WITH_TYPE(cldnn::generic_primitive) diff --git a/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.hpp b/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.hpp deleted file mode 100644 index f51e7813f9cd3c..00000000000000 --- a/src/plugins/intel_gpu/src/graph/impls/common/generic_primitive.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2018-2026 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include "registry/implementation_manager.hpp" -#include "program_node.h" - -#include - -namespace cldnn { - -namespace common { - -struct GenericPrimitiveImplementationManager : public ImplementationManager { - OV_GPU_PRIMITIVE_IMPL("common::generic_primitive") - GenericPrimitiveImplementationManager(shape_types shape_type, ValidateFunc vf = nullptr) : ImplementationManager(impl_types::common, shape_type, vf) {} - - std::unique_ptr create_impl(const program_node& node, const kernel_impl_params& params) const override; - - // in_out_fmts_t query_formats(const program_node& node) const override { - // std::vector in_fmts(node.get_dependencies().size(), format::any); - // std::vector out_fmts(node.get_outputs_count(), format::any); - - // for (size_t i = 0; i < node.get_dependencies().size(); i++) { - // size_t in_rank = node.get_input_layout(i).get_rank(); - // in_fmts[i] = format::get_default_format(in_rank); - // } - // size_t out_rank = node.get_output_layout().get_rank(); - // out_fmts[0] = format::get_default_format(out_rank); - - // return {in_fmts, out_fmts}; - // } -}; - -} // namespace common -} // namespace cldnn diff --git a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp new file mode 100644 index 00000000000000..51685ebe6c8942 --- /dev/null +++ b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp @@ -0,0 +1,167 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir_primitive.hpp" + +#include +#include + +#include "intel_gpu/primitives/mlir_primitive.hpp" +#include "intel_gpu/runtime/tensor_accessor.hpp" // cldnn::make_tensor +#include "mlir_primitive_inst.h" +#include "openvino/core/node.hpp" +#include "openvino/runtime/intel_gpu/ocl/ocl_wrapper.hpp" +#include "openvino/runtime/intel_gpu/remote_properties.hpp" +#include "openvino/runtime/internal_properties.hpp" +#include "register.hpp" +#include "registry/implementation_map.hpp" + +namespace cldnn::common { + +struct mlir_primitive_impl : typed_primitive_impl { + using parent = typed_primitive_impl; + using parent::parent; + + DECLARE_OBJECT_TYPE_SERIALIZATION(cldnn::common::mlir_primitive_impl) + + std::unique_ptr clone() const override { + return std::make_unique(*this); + } + + mlir_primitive_impl() : parent() {} + + explicit mlir_primitive_impl(const mlir_primitive_node& outer) { set_node_params(outer); } + + void set_node_params(const program_node& /*arg*/) override {} + + event::ptr execute_impl(const std::vector& dependent_events, + mlir_primitive_inst& instance) override { + auto& stream = instance.get_network().get_stream(); + const auto& prim = instance.node->get_primitive(); + const auto& op = prim->op; + + ov::TensorVector input_gpu_tensors; + ov::TensorVector output_gpu_tensors; + std::vector is_usm_ptr; + input_gpu_tensors.reserve(instance.inputs_memory_count()); + output_gpu_tensors.reserve(instance.outputs_memory_count()); + is_usm_ptr.reserve(instance.inputs_memory_count() + instance.outputs_memory_count()); + + auto process_buffer = [&stream, &is_usm_ptr](memory::ptr mem, ov::TensorVector& tensors) { + switch (mem->get_allocation_type()) { + case allocation_type::cl_mem: { + if (void* cl_buff = mem->get_handle()) { + tensors.push_back(make_tensor(mem->get_layout(), cl_buff)); + is_usm_ptr.push_back(false); + } else { + OPENVINO_THROW("Memory handle is null for cl_mem"); + } + break; + } + case allocation_type::usm_host: + case allocation_type::usm_shared: + case allocation_type::usm_device: { + auto usm_ptr = mem->buffer_ptr(); + // Seems to only occur with Out-Of-Order queues sometimes. Can't reproduce this anymore, uncomment if needed. + // HACK: force move to device, can we do better than this? + // auto gpu_buff = dynamic_cast(mem.get()); + // auto& usm_helper = gpu_buff->get_buffer().getUsmHelper(); + // usm_helper.enqueue_memcpy( + // dynamic_cast(stream).get_cl_queue(), + // usm_ptr, + // usm_ptr, + // mem->get_layout().bytes_count()); + tensors.push_back(make_tensor(mem->get_layout(), usm_ptr)); + is_usm_ptr.push_back(true); + break; + } + default: + OPENVINO_THROW("Unsupported memory type"); + } + }; + + for (size_t i = 0; i < instance.inputs_memory_count(); i++) { + process_buffer(instance.input_memory_ptr(i), input_gpu_tensors); + } + + for (size_t i = 0; i < instance.outputs_memory_count(); i++) { + process_buffer(instance.output_memory_ptr(i), output_gpu_tensors); + } + + ov::EvaluationContext meta; + if (void* queue = stream.get_handle()) { + meta.insert(ov::intel_gpu::ocl_queue(queue)); + } else { + OPENVINO_THROW("Unsupported queue type"); + } + meta.insert(ov::internal::mlir_meta::is_kernel_arg_usm(is_usm_ptr)); + + std::vector events_list; + cl_event* result_event = nullptr; + if (stream.get_queue_type() == QueueTypes::out_of_order) { + events_list.reserve(dependent_events.size() + 1); + for (auto& ev : dependent_events) { + if (void* cl_ev = ev->get_handle()) { + events_list.push_back(cl_ev); + } else { + OPENVINO_THROW("Unsupported event type"); + } + } + meta.insert(ov::internal::mlir_meta::wait_list(events_list)); + // 'cl_event' is a pointer itself, that's why we pass pointer to a pointer here. + meta.insert(ov::internal::mlir_meta::result_event(reinterpret_cast(result_event))); + } + + OPENVINO_ASSERT(op->evaluate( + output_gpu_tensors, input_gpu_tensors, meta), + "[GPU] Couldn't execute MLIROp ", op->get_friendly_name()); + + event::ptr ev; + if (stream.get_queue_type() == QueueTypes::out_of_order) { + OPENVINO_ASSERT(result_event != nullptr, "Result cl_event is not set"); + ev = stream.create_base_event(*result_event); + } else { + ev = stream.create_user_event(true); + } + + return ev; + } + + static std::unique_ptr create(const mlir_primitive_node& arg, + const kernel_impl_params& /*params*/) { + return std::make_unique(arg); + } + + void init_kernels(const kernels_cache&, const kernel_impl_params&) override {} + + void save(BinaryOutputBuffer& ob) const override { parent::save(ob); } + void load(BinaryInputBuffer& ib) override { parent::load(ib); } + + bool is_cpu() const override { return false; } +}; + +std::unique_ptr MLIRPrimitiveImplementationManager::create_impl( + const program_node& node, + const kernel_impl_params& params) const { + assert(node.is_type()); + return mlir_primitive_impl::create(static_cast(node), params); +} + +namespace detail { + +attach_mlir_primitive_common::attach_mlir_primitive_common() { + implementation_map::add(impl_types::common, + shape_types::dynamic_shape, + mlir_primitive_impl::create, + {}, + {}); + implementation_map::add(impl_types::common, mlir_primitive_impl::create, {}); +} + +} // namespace detail + +} // namespace cldnn::common + +BIND_BINARY_BUFFER_WITH_TYPE(cldnn::common::mlir_primitive_impl) +BIND_BINARY_BUFFER_WITH_TYPE(cldnn::mlir_primitive) diff --git a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.hpp b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.hpp new file mode 100644 index 00000000000000..8bec32e7e20b0c --- /dev/null +++ b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "program_node.h" +#include "registry/implementation_manager.hpp" + +namespace cldnn::common { + +struct MLIRPrimitiveImplementationManager : public ImplementationManager { + OV_GPU_PRIMITIVE_IMPL("common::mlir_primitive") + MLIRPrimitiveImplementationManager(shape_types shape_type, ValidateFunc vf = nullptr) + : ImplementationManager(impl_types::common, shape_type, vf) {} + + std::unique_ptr create_impl(const program_node& node, + const kernel_impl_params& params) const override; +}; + +} // namespace cldnn::common diff --git a/src/plugins/intel_gpu/src/graph/impls/common/register.cpp b/src/plugins/intel_gpu/src/graph/impls/common/register.cpp index 16546d495adcf6..767bb012d9502e 100644 --- a/src/plugins/intel_gpu/src/graph/impls/common/register.cpp +++ b/src/plugins/intel_gpu/src/graph/impls/common/register.cpp @@ -13,9 +13,9 @@ namespace common { void register_implementations() { REGISTER_COMMON(condition); REGISTER_COMMON(data); - REGISTER_COMMON(generic_primitive); REGISTER_COMMON(input_layout); REGISTER_COMMON(loop); + REGISTER_COMMON(mlir_primitive); } } // namespace common diff --git a/src/plugins/intel_gpu/src/graph/impls/common/register.hpp b/src/plugins/intel_gpu/src/graph/impls/common/register.hpp index 8df9f4b6084353..551d1d0da7d6fc 100644 --- a/src/plugins/intel_gpu/src/graph/impls/common/register.hpp +++ b/src/plugins/intel_gpu/src/graph/impls/common/register.hpp @@ -24,9 +24,9 @@ namespace detail { REGISTER_COMMON(condition); REGISTER_COMMON(data); -REGISTER_COMMON(generic_primitive); REGISTER_COMMON(input_layout); REGISTER_COMMON(loop); +REGISTER_COMMON(mlir_primitive); #undef REGISTER_COMMON diff --git a/src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h b/src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h deleted file mode 100644 index d9ea8e59e6c9ed..00000000000000 --- a/src/plugins/intel_gpu/src/graph/include/generic_primitive_inst.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once -#include "intel_gpu/primitives/generic_primitive.hpp" -#include "primitive_inst.h" - -#include - -namespace cldnn { - -template <> -struct typed_program_node : public typed_program_node_base { - using parent = typed_program_node_base; - -public: - using parent::parent; - - program_node& input(size_t idx = 0) const { return get_dependency(idx); } -}; - -using generic_primitive_node = typed_program_node; - -template <> -class typed_primitive_inst : public typed_primitive_inst_base { - using parent = typed_primitive_inst_base; - using parent::parent; - -public: - template - static std::vector calc_output_layouts(generic_primitive_node const& node, const kernel_impl_params& impl_param); - static layout calc_output_layout(generic_primitive_node const& node, kernel_impl_params const& impl_param); - - static std::string to_string(generic_primitive_node const& node); - - typed_primitive_inst(network& network, generic_primitive_node const& node); - - const generic_primitive_node* node; -}; - -using generic_primitive_inst = typed_primitive_inst; - -} // namespace cldnn diff --git a/src/plugins/intel_gpu/src/graph/include/mlir_primitive_inst.h b/src/plugins/intel_gpu/src/graph/include/mlir_primitive_inst.h new file mode 100644 index 00000000000000..24fcc7d6ab2a86 --- /dev/null +++ b/src/plugins/intel_gpu/src/graph/include/mlir_primitive_inst.h @@ -0,0 +1,46 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "intel_gpu/primitives/mlir_primitive.hpp" +#include "primitive_inst.h" + +namespace cldnn { + +template <> +struct typed_program_node : public typed_program_node_base { + using parent = typed_program_node_base; + +public: + using parent::parent; + + program_node& input(size_t idx = 0) const { return get_dependency(idx); } +}; + +using mlir_primitive_node = typed_program_node; + +template <> +class typed_primitive_inst : public typed_primitive_inst_base { + using parent = typed_primitive_inst_base; + using parent::parent; + +public: + template + static std::vector calc_output_layouts(mlir_primitive_node const& node, + const kernel_impl_params& impl_param); + static layout calc_output_layout(mlir_primitive_node const& node, kernel_impl_params const& impl_param); + + static std::string to_string(mlir_primitive_node const& node); + + typed_primitive_inst(network& network, mlir_primitive_node const& node); + + const mlir_primitive_node* node; +}; + +using mlir_primitive_inst = typed_primitive_inst; + +} // namespace cldnn diff --git a/src/plugins/intel_gpu/src/graph/mlir_primitive.cpp b/src/plugins/intel_gpu/src/graph/mlir_primitive.cpp new file mode 100644 index 00000000000000..40204fe845032e --- /dev/null +++ b/src/plugins/intel_gpu/src/graph/mlir_primitive.cpp @@ -0,0 +1,67 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir_primitive_inst.h" + +#include +#include + +#include "json_object.h" +#include "openvino/core/partial_shape.hpp" +#include "primitive_type_base.h" + +namespace cldnn { + +GPU_DEFINE_PRIMITIVE_TYPE_ID(mlir_primitive) + +layout mlir_primitive_inst::calc_output_layout(const mlir_primitive_node& node, + const kernel_impl_params& impl_param) { + return calc_output_layouts(node, impl_param)[0]; +} + +template +std::vector mlir_primitive_inst::calc_output_layouts(mlir_primitive_node const& /*node*/, + const kernel_impl_params& impl_param) { + auto prim = impl_param.typed_desc(); + + std::vector input_shapes; + input_shapes.reserve(impl_param.input_layouts.size()); + for (const auto& l : impl_param.input_layouts) { + input_shapes.push_back(l.get()); + } + + std::vector output_shapes = prim->shape_infer_f(input_shapes); + + std::vector out_layouts; + out_layouts.reserve(output_shapes.size()); + for (size_t i = 0; i < output_shapes.size(); ++i) { + out_layouts.emplace_back(output_shapes[i], + prim->get_output_data_type(i).value(), + format::get_default_format(output_shapes[i].size())); + } + return out_layouts; +} + +template std::vector mlir_primitive_inst::calc_output_layouts( + mlir_primitive_node const&, const kernel_impl_params&); + +std::string mlir_primitive_inst::to_string(mlir_primitive_node const& node) { + auto node_info = node.desc_to_json(); + std::stringstream primitive_description; + json_composite mlir_info; + if (const auto& op = node.get_primitive()->op) { + mlir_info.add("subgraph_name", op->get_friendly_name()); + } + mlir_info.add("num_outputs", std::to_string(node.get_primitive()->num_outputs)); + node_info->add("mlir_primitive_info", mlir_info); + node_info->dump(primitive_description); + return primitive_description.str(); +} + +typed_primitive_inst::typed_primitive_inst(network& network, + const mlir_primitive_node& node) + : parent(network, node), + node(&node) {} + +} // namespace cldnn diff --git a/src/plugins/intel_gpu/src/graph/registry/generic_primitive_impls.cpp b/src/plugins/intel_gpu/src/graph/registry/mlir_primitive_impls.cpp similarity index 52% rename from src/plugins/intel_gpu/src/graph/registry/generic_primitive_impls.cpp rename to src/plugins/intel_gpu/src/graph/registry/mlir_primitive_impls.cpp index c4ebd305f9937e..ee72f18bb603d8 100644 --- a/src/plugins/intel_gpu/src/graph/registry/generic_primitive_impls.cpp +++ b/src/plugins/intel_gpu/src/graph/registry/mlir_primitive_impls.cpp @@ -2,25 +2,23 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "registry.hpp" -#include "intel_gpu/primitives/generic_primitive.hpp" +#include "intel_gpu/primitives/mlir_primitive.hpp" #include "primitive_inst.h" +#include "registry.hpp" #if OV_GPU_WITH_COMMON - #include "impls/common/generic_primitive.hpp" +# include "impls/common/mlir_primitive.hpp" #endif - namespace ov::intel_gpu { using namespace cldnn; -const std::vector>& Registry::get_implementations() { +const std::vector>& Registry::get_implementations() { static const std::vector> impls = { - OV_GPU_CREATE_INSTANCE_COMMON(common::GenericPrimitiveImplementationManager, shape_types::static_shape) - OV_GPU_CREATE_INSTANCE_COMMON(common::GenericPrimitiveImplementationManager, shape_types::dynamic_shape) + OV_GPU_CREATE_INSTANCE_COMMON(common::MLIRPrimitiveImplementationManager, shape_types::static_shape) + OV_GPU_CREATE_INSTANCE_COMMON(common::MLIRPrimitiveImplementationManager, shape_types::dynamic_shape) }; - return impls; } diff --git a/src/plugins/intel_gpu/src/graph/registry/registry.hpp b/src/plugins/intel_gpu/src/graph/registry/registry.hpp index 190d79a8ca663f..bb751f7f2f59b4 100644 --- a/src/plugins/intel_gpu/src/graph/registry/registry.hpp +++ b/src/plugins/intel_gpu/src/graph/registry/registry.hpp @@ -150,7 +150,6 @@ REGISTER_IMPLS(gather); REGISTER_IMPLS(gather_nd); REGISTER_IMPLS(gated_delta_net); REGISTER_IMPLS(gemm); -REGISTER_IMPLS(generic_primitive); REGISTER_IMPLS(group_normalization); REGISTER_IMPLS(loop); REGISTER_IMPLS(lora); @@ -189,6 +188,7 @@ REGISTER_IMPLS(moe_gemm); REGISTER_IMPLS(moe_scatter_reduction); REGISTER_IMPLS(moe_gather); REGISTER_IMPLS(gather_matmul); +REGISTER_IMPLS(mlir_primitive); REGISTER_DEFAULT_IMPLS(assign, CPU_S, CPU_D); REGISTER_DEFAULT_IMPLS(read_value, CPU_S, CPU_D); diff --git a/src/plugins/intel_gpu/src/plugin/ops/generic.cpp b/src/plugins/intel_gpu/src/plugin/ops/generic.cpp deleted file mode 100644 index a323bc8f8e4bee..00000000000000 --- a/src/plugins/intel_gpu/src/plugin/ops/generic.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// -#include "intel_gpu/plugin/common_utils.hpp" -#include "intel_gpu/runtime/internal_properties.hpp" -#include "intel_gpu/runtime/tensor_accessor.hpp" -#include "openvino/core/partial_shape.hpp" -#include "intel_gpu/plugin/program_builder.hpp" -#include "intel_gpu/primitives/generic_primitive.hpp" - -namespace ov { -namespace intel_gpu { - -void CreateGenericOp(ProgramBuilder& p, const std::shared_ptr& op) { - cldnn::generic_primitive::execute_function execute_f = [op]( - const std::vector& dependent_events, - cldnn::stream& stream, - const std::vector& inputs, - const std::vector& outputs) { - // Synchronization as evalute() may be a CPU code - if (stream.get_queue_type() == cldnn::QueueTypes::out_of_order) { - for (auto& ev : dependent_events) { - ev->wait(); - } - } else { - stream.finish(); - } - - cldnn::event::ptr ev = stream.create_user_event(false); - - ov::TensorVector input_host_tensors; - ov::TensorVector output_host_tensors; - - for (size_t i = 0; i < inputs.size(); i++) - input_host_tensors.push_back(make_tensor(inputs[i]->get_layout(), inputs[i]->lock(stream, cldnn::mem_lock_type::read))); - - for (size_t i = 0; i < outputs.size(); i++) - output_host_tensors.push_back(make_tensor(outputs[i]->get_layout(), outputs[i]->lock(stream, cldnn::mem_lock_type::write))); - - OPENVINO_ASSERT(op->evaluate(output_host_tensors, input_host_tensors), - "[GPU] Couldn't execute GenericOp ", op->get_friendly_name()); - - for (size_t i = 0; i < inputs.size(); i++) - inputs[i]->unlock(stream); - - for (size_t i = 0; i < outputs.size(); i++) - outputs[i]->unlock(stream); - - ev->set(); - return ev; - }; - cldnn::generic_primitive::shape_infer_function shape_infer_f = [&op]( - const std::vector& input_shapes) -> std::vector { - // Dummy shape infer - return {input_shapes[0]}; - }; - - auto inputs = p.GetInputInfo(op); - const std::string layerName = layer_type_name_ID(op); - const size_t num_outputs = op->get_output_size(); - - const cldnn::generic_primitive primitive(layerName, - inputs, - execute_f, - shape_infer_f, - num_outputs, - get_output_data_types(op)); - - p.add_primitive(*op, primitive); -} - -} // namespace intel_gpu -} // namespace ov diff --git a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp index 177e52ef783aff..e67d28a75a062d 100644 --- a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp +++ b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp @@ -1,142 +1,48 @@ // Copyright (C) 2023 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // -#include "intel_gpu/plugin/common_utils.hpp" -#include "intel_gpu/runtime/internal_properties.hpp" -#include "intel_gpu/runtime/tensor_accessor.hpp" -#include "openvino/core/partial_shape.hpp" -#include "intel_gpu/plugin/program_builder.hpp" -#include "intel_gpu/primitives/generic_primitive.hpp" - -#include "openvino/runtime/intel_gpu/ocl/ocl_wrapper.hpp" -#include "openvino/runtime/intel_gpu/remote_properties.hpp" -#include "openvino/runtime/internal_properties.hpp" - -#include "transformations/mlir/convert.hpp" - -namespace ov { -namespace op { -namespace mlir { -using MLIRSubgraph = ov::op::Op; -} // namespace mlir -} // namespace op -} // namespace ov - -namespace ov { -namespace intel_gpu { - -void CreateMLIRSubgraphOp(ProgramBuilder& p, const std::shared_ptr& op) { - cldnn::generic_primitive::execute_function execute_f = [op]( - const std::vector& dependent_events, - cldnn::stream& stream, - const std::vector& inputs, - const std::vector& outputs) { - ov::TensorVector input_gpu_tensors; - ov::TensorVector output_gpu_tensors; - std::vector is_usm_ptr; - input_gpu_tensors.reserve(inputs.size()); - output_gpu_tensors.reserve(outputs.size()); - is_usm_ptr.reserve(inputs.size() + outputs.size()); - - auto process_buffer = [&stream, &is_usm_ptr](cldnn::memory::ptr mem, ov::TensorVector& tensors) { - switch (mem->get_allocation_type()) { - case cldnn::allocation_type::cl_mem: { - if (void* cl_buff = mem->get_handle()) { - tensors.push_back(make_tensor(mem->get_layout(), cl_buff)); - is_usm_ptr.push_back(false); - } else { - OPENVINO_THROW("Memory handle is null for cl_mem"); - } - break; - } - case cldnn::allocation_type::usm_host: - case cldnn::allocation_type::usm_shared: - case cldnn::allocation_type::usm_device: { - auto usm_ptr = mem->buffer_ptr(); - // Seems to only occur with Out-Of-Order queues sometimes. Can't reproduce this anymore, uncomment if needed. - // HACK: force move to device, can we do better than this? - // auto gpu_buff = dynamic_cast(mem.get()); - // auto& usm_helper = gpu_buff->get_buffer().getUsmHelper(); - // usm_helper.enqueue_memcpy( - // dynamic_cast(stream).get_cl_queue(), - // usm_ptr, - // usm_ptr, - // mem->get_layout().bytes_count()); - tensors.push_back(make_tensor(mem->get_layout(), usm_ptr)); - is_usm_ptr.push_back(true); - break; - } - default: - OPENVINO_THROW("Unsupported memory type"); - } - }; - - for (size_t i = 0; i < inputs.size(); i++) { - process_buffer(inputs[i], input_gpu_tensors); - } - for (size_t i = 0; i < outputs.size(); i++) { - process_buffer(outputs[i], output_gpu_tensors); - } - - ov::EvaluationContext meta; - if (void* queue = stream.get_handle()) { - meta.insert(ov::intel_gpu::ocl_queue(queue)); - } else { - OPENVINO_THROW("Unsupported queue type"); - } - meta.insert(ov::internal::mlir_meta::is_kernel_arg_usm(is_usm_ptr)); - - std::vector events_list; - cl_event* result_event = nullptr; - if (stream.get_queue_type() == cldnn::QueueTypes::out_of_order) { - events_list.reserve(dependent_events.size() + 1); - for (auto& ev : dependent_events) { - if (void* cl_ev = ev->get_handle()) { - events_list.push_back(cl_ev); - } else { - OPENVINO_THROW("Unsupported event type"); - } - } - meta.insert(ov::internal::mlir_meta::wait_list(events_list)); - // 'cl_event' is a pointer itself, that's why we pass pointer to a pointer here. - meta.insert(ov::internal::mlir_meta::result_event(reinterpret_cast(result_event))); - } - - OPENVINO_ASSERT(op->evaluate( - output_gpu_tensors, input_gpu_tensors, meta), - "[GPU] Couldn't execute MLIROp ", op->get_friendly_name()); - - cldnn::event::ptr ev; - if (stream.get_queue_type() == cldnn::QueueTypes::out_of_order) { - OPENVINO_ASSERT(result_event != nullptr, "Result cl_event is not set"); - ev = stream.create_base_event(*result_event); - } else { - ev = stream.create_user_event(true); - } - - return ev; - }; - cldnn::generic_primitive::shape_infer_function shape_infer_f = [op]( - const std::vector& input_shapes) -> std::vector { - return ov::mlir::mlir_op_shape_infer(op, input_shapes); - }; +#ifdef GRAPH_COMPILER +#include "intel_gpu/plugin/common_utils.hpp" +#include "intel_gpu/plugin/program_builder.hpp" +#include "intel_gpu/primitives/mlir_primitive.hpp" +#include "transformations/mlir/convert.hpp" // ov::mlir::mlir_op_shape_infer +#include "transformations/mlir/mlir_op.hpp" + +// REGISTER_FACTORY_IMPL(internal, MLIR) expands to: +// * RegisterFactory (requires the alias below) +// * a call to Create##MLIR##Op == CreateMLIROp +// We use op_name = "MLIR" (not "MLIROp") to keep the "Op" suffix that the +// macro concatenates and match the Gemm/KVCache/etc. naming convention. +namespace ov::op::internal { +using MLIR = ov::mlir::MLIROp; +} // namespace ov::op::internal + +namespace ov::intel_gpu { + +static void CreateMLIROp(ProgramBuilder& p, const std::shared_ptr& op) { auto inputs = p.GetInputInfo(op); - const std::string layerName = layer_type_name_ID(op); + const std::string layer_name = layer_type_name_ID(op); const size_t num_outputs = op->get_output_size(); - const cldnn::generic_primitive primitive(layerName, - inputs, - execute_f, - shape_infer_f, - num_outputs, - get_output_data_types(op)); + cldnn::mlir_primitive::shape_infer_function shape_infer_f = + [op](const std::vector& input_shapes) { + return ov::mlir::mlir_op_shape_infer(op, input_shapes); + }; + + cldnn::mlir_primitive primitive(layer_name, + inputs, + op, // shared_ptr + std::move(shape_infer_f), + num_outputs, + get_output_data_types(op)); p.add_primitive(*op, primitive); } -REGISTER_FACTORY_IMPL(mlir, MLIRSubgraph); +REGISTER_FACTORY_IMPL(internal, MLIR); + +} // namespace ov::intel_gpu -} // namespace intel_gpu -} // namespace ov +#endif // GRAPH_COMPILER diff --git a/src/plugins/intel_gpu/src/plugin/program_builder.cpp b/src/plugins/intel_gpu/src/plugin/program_builder.cpp index f604422f104da6..4c6fd7e235a90d 100644 --- a/src/plugins/intel_gpu/src/plugin/program_builder.cpp +++ b/src/plugins/intel_gpu/src/plugin/program_builder.cpp @@ -231,15 +231,7 @@ void ProgramBuilder::CreateSingleLayerPrimitive(const std::shared_ptr& ov::write_all_to_stream(ss, "Operation: ", op->get_friendly_name(), " of type ", op->get_type_name(), "(", op->get_type_info().version_id, ") is not supported."); - if (op->has_evaluate()) { - std::cout << ss.str() << " Fallback to Op::evaluate()" << std::endl; - // If MLIROp - CreateMLIRSubgraphOp(*this, std::dynamic_pointer_cast(op)); - // else - // CreateGenericOp(*this, op); - } else { - OPENVINO_THROW(ss.str()); - } + OPENVINO_THROW(ss.str()); } } From a338296ffa04e82fb11881a850a519cf6399367a Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Thu, 16 Jul 2026 14:03:45 +0000 Subject: [PATCH 083/121] Fixed profiling info for MLIROp --- .github/workflows/graph-compiler.yml | 2 +- .../openvino/runtime/internal_properties.hpp | 10 ++-- .../src/graph/impls/common/mlir_primitive.cpp | 47 +++++++++++++------ .../plugin/transformations/mlir/mlir_op.cpp | 43 ++++++++++------- .../plugin/transformations/mlir/mlir_op.hpp | 4 +- .../mlir_op/matmul_rms_norm_concat.cpp | 11 ++++- .../tests/functional/mlir_op/sanity_tests.cpp | 19 -------- .../shared_test_classes/base/benchmark.hpp | 33 +++---------- 8 files changed, 80 insertions(+), 89 deletions(-) diff --git a/.github/workflows/graph-compiler.yml b/.github/workflows/graph-compiler.yml index 268c89545785b8..3706aebb3e0f94 100644 --- a/.github/workflows/graph-compiler.yml +++ b/.github/workflows/graph-compiler.yml @@ -9,7 +9,7 @@ permissions: env: GC_REPO: https://x-access-token:${{ secrets.GC_TOKEN }}@github.com/intel-sandbox/graph-compiler - GC_TAG: main + GC_TAG: ap/ocl-events BUILD_DIR: ${{ github.workspace }}-gc-build OUTPUT_DIR: ${{ github.workspace }}-gc-bin diff --git a/src/inference/dev_api/openvino/runtime/internal_properties.hpp b/src/inference/dev_api/openvino/runtime/internal_properties.hpp index fff6f95b5c6bcf..404a30e3f082c2 100644 --- a/src/inference/dev_api/openvino/runtime/internal_properties.hpp +++ b/src/inference/dev_api/openvino/runtime/internal_properties.hpp @@ -188,15 +188,11 @@ namespace mlir_meta { static constexpr Property> wait_list{"EVENTS_WAIT_LIST"}; /** - * @brief This key identifies a pointer to a cl_enevt that should be set with - * the result cl_event of a kernel execution. Example: - * @code - * cl_event result_event = launchModuleAndGetEvent(); - * cl_event* ev = evaluationContext[ov::internal::mlir_meta::result_event.name()].as(); - * *ev = result_event; + * @brief This key identifies a pointer to a list that should be filled with + * result cl_events of a kernel execution. * @ingroup ov_dev_api_plugin_mlir_meta_api */ -static constexpr Property result_event{"RESULT_EVENT"}; +static constexpr Property*> result_events{"RESULT_EVENTS"}; /** * @brief This key identifies whether the kernel argument at [i] position is USM pointer diff --git a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp index 51685ebe6c8942..9ee165d8dc271c 100644 --- a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp +++ b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp @@ -8,6 +8,7 @@ #include #include "intel_gpu/primitives/mlir_primitive.hpp" +#include "intel_gpu/runtime/stream.hpp" #include "intel_gpu/runtime/tensor_accessor.hpp" // cldnn::make_tensor #include "mlir_primitive_inst.h" #include "openvino/core/node.hpp" @@ -48,7 +49,7 @@ struct mlir_primitive_impl : typed_primitive_impl { output_gpu_tensors.reserve(instance.outputs_memory_count()); is_usm_ptr.reserve(instance.inputs_memory_count() + instance.outputs_memory_count()); - auto process_buffer = [&stream, &is_usm_ptr](memory::ptr mem, ov::TensorVector& tensors) { + auto process_buffer = [&is_usm_ptr](memory::ptr mem, ov::TensorVector& tensors) { switch (mem->get_allocation_type()) { case allocation_type::cl_mem: { if (void* cl_buff = mem->get_handle()) { @@ -98,34 +99,52 @@ struct mlir_primitive_impl : typed_primitive_impl { meta.insert(ov::internal::mlir_meta::is_kernel_arg_usm(is_usm_ptr)); std::vector events_list; - cl_event* result_event = nullptr; + std::vector result_events; + const bool need_result_events = instance.get_config().get_enable_profiling() || + stream.get_queue_type() == QueueTypes::out_of_order; + if (need_result_events) { + meta.insert(ov::internal::mlir_meta::result_events(&result_events)); + } + event::ptr marker; if (stream.get_queue_type() == QueueTypes::out_of_order) { - events_list.reserve(dependent_events.size() + 1); + std::vector depends; + depends.reserve(dependent_events.size()); for (auto& ev : dependent_events) { + if (!ev) { + continue; + } if (void* cl_ev = ev->get_handle()) { events_list.push_back(cl_ev); } else { - OPENVINO_THROW("Unsupported event type"); + depends.push_back(ev); + } + } + if (!depends.empty()) { + marker = stream.enqueue_marker(depends, true); + if (void* cl_ev = marker->get_handle()) { + events_list.push_back(cl_ev); } } - meta.insert(ov::internal::mlir_meta::wait_list(events_list)); - // 'cl_event' is a pointer itself, that's why we pass pointer to a pointer here. - meta.insert(ov::internal::mlir_meta::result_event(reinterpret_cast(result_event))); + if (!events_list.empty()) { + meta.insert(ov::internal::mlir_meta::wait_list(events_list)); + } } OPENVINO_ASSERT(op->evaluate( output_gpu_tensors, input_gpu_tensors, meta), "[GPU] Couldn't execute MLIROp ", op->get_friendly_name()); - event::ptr ev; - if (stream.get_queue_type() == QueueTypes::out_of_order) { - OPENVINO_ASSERT(result_event != nullptr, "Result cl_event is not set"); - ev = stream.create_base_event(*result_event); - } else { - ev = stream.create_user_event(true); + if (!result_events.empty()) { + std::vector events; + events.reserve(result_events.size()); + for (auto event : result_events) { + events.push_back(stream.create_base_event(event)); + } + return stream.aggregate_events(events, true); } - return ev; + OPENVINO_ASSERT(!need_result_events, "Result cl_events are not set"); + return stream.create_user_event(true); } static std::unique_ptr create(const mlir_primitive_node& arg, diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp index 8ba83474f9a89a..099e95189dd286 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp @@ -161,7 +161,8 @@ MLIREvaluateGcGPU::MLIREvaluateGcGPU(OwningOpRef _module, std::s }; bool MLIREvaluateGcGPU::invoke(const ov::TensorVector& inputs, ov::TensorVector& outputs, const ov::EvaluationContext& evaluationContext) { - gc::gpu::OclContext ctx = build_ocl_context(evaluationContext); + std::vector waitList; + gc::gpu::OclContext ctx = build_ocl_context(evaluationContext, waitList); gc::gpu::StaticExecutor exec(module); auto it = evaluationContext.find(ov::internal::mlir_meta::is_kernel_arg_usm.name()); @@ -178,12 +179,14 @@ bool MLIREvaluateGcGPU::invoke(const ov::TensorVector& inputs, ov::TensorVector& } exec(ctx); - maybe_set_result_event(evaluationContext, ctx); + + maybe_set_result_events(evaluationContext, ctx); return true; } bool MLIREvaluateGcGPU::invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) { - gc::gpu::OclContext ctx = build_ocl_context(evaluationContext); + std::vector waitList; + gc::gpu::OclContext ctx = build_ocl_context(evaluationContext, waitList); gc::gpu::DynamicExecutor exec(module); // Layout (5 pointers per memref, see MemRefDescriptor::append_to_packed_args): @@ -201,23 +204,31 @@ bool MLIREvaluateGcGPU::invoke_packed(std::vector& args, const ov::Evalua ); } exec(ctx); - maybe_set_result_event(evaluationContext, ctx); + maybe_set_result_events(evaluationContext, ctx); return true; } -void MLIREvaluateGcGPU::maybe_set_result_event(const ov::EvaluationContext& evaluationContext, gc::gpu::OclContext& ctx) { - // case with in-order queue where we don't need to return an event - if (ctx.lastEvent == nullptr) +void MLIREvaluateGcGPU::maybe_set_result_events(const ov::EvaluationContext& evaluationContext, gc::gpu::OclContext& ctx) { + auto events_it = evaluationContext.find(ov::internal::mlir_meta::result_events.name()); + if (events_it == evaluationContext.end()) return; - auto it = evaluationContext.find(ov::internal::mlir_meta::result_event.name()); - if (it == evaluationContext.end()) { - OPENVINO_THROW("No result_event provided for OpenCL execution"); + + auto retain_event = [](cl_event event) { + const auto err = clRetainEvent(event); + if (err != CL_SUCCESS) { + OPENVINO_THROW("Failed to retain MLIR result event, error: ", err); + } + }; + + auto* events = events_it->second.as*>(); + events->reserve(events->size() + ctx.events.size()); + for (auto event : ctx.events) { + retain_event(event); + events->push_back(event); } - cl_event* ev = reinterpret_cast(it->second.as()); - *ev = ctx.lastEvent; } -gc::gpu::OclContext MLIREvaluateGcGPU::build_ocl_context(const ov::EvaluationContext& evaluationContext) { +gc::gpu::OclContext MLIREvaluateGcGPU::build_ocl_context(const ov::EvaluationContext& evaluationContext, std::vector& waitList) { auto it = evaluationContext.find(ov::intel_gpu::ocl_queue.name()); if (it == evaluationContext.end()) { OPENVINO_THROW("No queue provided for OpenCL execution"); @@ -225,17 +236,15 @@ gc::gpu::OclContext MLIREvaluateGcGPU::build_ocl_context(const ov::EvaluationCon cl_command_queue queue = reinterpret_cast(it->second.as()); uint32_t waitListLen = 0; - std::vector waitList; - bool foundWaitList = false; it = evaluationContext.find(ov::internal::mlir_meta::wait_list.name()); if (it != evaluationContext.end()) { waitList = it->second.as>(); waitListLen = waitList.size(); - foundWaitList = true; } - return gc::gpu::OclContext(module->runtime, queue, /*createEvents=*/foundWaitList, + const bool createEvents = evaluationContext.count(ov::internal::mlir_meta::result_events.name()) != 0; + return gc::gpu::OclContext(module->runtime, queue, createEvents, waitListLen, reinterpret_cast(waitList.data())); } diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.hpp index 46ebd065c0e1b4..e400b7db26d0d5 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.hpp @@ -42,8 +42,8 @@ class MLIREvaluateGcGPU : public MLIREvaluateBase { bool invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) override; private: - gc::gpu::OclContext build_ocl_context(const ov::EvaluationContext& evaluationContext); - static void maybe_set_result_event(const ov::EvaluationContext& evaluationContext, gc::gpu::OclContext& ctx); + gc::gpu::OclContext build_ocl_context(const ov::EvaluationContext& evaluationContext, std::vector& waitList); + static void maybe_set_result_events(const ov::EvaluationContext& evaluationContext, gc::gpu::OclContext& ctx); }; // Maps [output index][dimension index] -> [input index][dimension index] to infer shapes for entire subgraph diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp index 1e0f3444851f7c..c1ed538f7e327d 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp @@ -17,6 +17,7 @@ #include "openvino/op/transpose.hpp" #include "shared_test_classes/base/benchmark.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" +#include "transformations/mlir/convert.hpp" namespace { @@ -115,7 +116,10 @@ TEST_P(MatMulRmsnormTest, Inference) { run(); } TEST_P(MatMulRmsnormBenchmark, Inference) { - run_benchmark("MLIROp"); + if (ov::pass::is_mlir_transform_enabled()) + run_benchmark("MLIROp"); + else + run_benchmark({"FullyConnected", "Add", "Reshape", "Transpose", "RMS"}); } const auto rmsnormParams = ::testing::Combine(::testing::Values(ov::Shape{1, 1024, 1536}), ::testing::Values(ov::Shape{1536, 1536})); @@ -171,7 +175,10 @@ TEST_P(MatMulRmsnormConcatTest, Inference) { run(); } TEST_P(MatMulRmsnormConcatBenchmark, Inference) { - run_benchmark("MLIROp"); + if (ov::pass::is_mlir_transform_enabled()) + run_benchmark("MLIROp"); + else + run_benchmark({"FullyConnected", "Add", "Reshape", "Transpose", "RMS", "Concat"}); } const auto concatParams = diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp index 91760642eac9fb..2207967d86a9f9 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp @@ -269,10 +269,6 @@ static std::map allocate_input_tensors( } TEST(MLIRExecution, SDPABasic) { - auto mode = ov::util::getenv_string("OV_MLIR_MODE"); - if (mode != "GC_GPU" && mode != "GC") - GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " - << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; const ov::PartialShape query_shape{4, 4096, 64}; const ov::PartialShape key_shape{4, 4096, 64}; const ov::PartialShape value_shape{4, 4096, 64}; @@ -359,11 +355,6 @@ TEST(MLIRExecution, SDPABasic) { } TEST(MLIRExecution, SimpleMatmulf32) { - auto mode = ov::util::getenv_string("OV_MLIR_MODE"); - if (mode != "GC_GPU" && mode != "GC") - GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " - << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; - ov::Core core; auto model = core.read_model( model_full_path("matmul_64_128_f32.xml")); @@ -405,11 +396,6 @@ TEST(MLIRExecution, SimpleMatmulf32) { } TEST(MLIRExecution, SimpleMatmulf32CLBuffer) { - auto mode = ov::util::getenv_string("OV_MLIR_MODE"); - if (mode != "GC_GPU" && mode != "GC") - GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " - << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; - ov::Core core; auto model = core.read_model( model_full_path("matmul_64_128_f32.xml")); @@ -451,11 +437,6 @@ TEST(MLIRExecution, SimpleMatmulf32CLBuffer) { } TEST(MLIRExecution, SimpleMatmulf16) { - auto mode = ov::util::getenv_string("OV_MLIR_MODE"); - if (mode != "GC_GPU" && mode != "GC") - GTEST_SKIP() << "This test is only for GC or GC_GPU MLIR modes. " - << "Set 'OV_MLIR_MODE' env variable to 'GC' or 'GC_GPU'"; - ov::Core core; auto model = core.read_model( model_full_path("matmul_64_128_f16.xml")); diff --git a/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp b/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp index fe112e40548641..dd4653a1a7f52f 100644 --- a/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp +++ b/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp @@ -146,42 +146,21 @@ class BenchmarkLayerTest : public BaseLayerTest { // Benchmark for (int i = 0; i < num_attempts_; ++i) { -#ifdef GRAPH_COMPILER - const auto start = std::chrono::steady_clock::now(); this->inferRequest.infer(); - const auto end = std::chrono::steady_clock::now(); -#else - this->inferRequest.infer(); -#endif const auto& profiling_info = this->inferRequest.get_profiling_info(); for (auto& res : results_us) { const std::string node_type_name = res.first; uint64_t& time = res.second; - auto found_profile = std::find_if(profiling_info.begin(), profiling_info.end(), - [&node_type_name](const ProfilingInfo& profile) { - return profile.node_type == node_type_name; - }); -#ifdef GRAPH_COMPILER - if (node_type_name == "MLIROp") { - uint64_t profile_time = 0; - for (const auto& profile : profiling_info) { - if (profile.node_type == "Parameter" || profile.node_type == "Result") { - continue; - } - profile_time += profile.real_time.count(); + bool found_profile = false; + for (const auto& profile : profiling_info) { + if (profile.node_type == node_type_name) { + time += profile.real_time.count(); + found_profile = true; } - - if (profile_time) - time += profile_time; - else - time += std::chrono::duration_cast(end - start).count(); - continue; } -#endif - if (found_profile == profiling_info.end()) { + if (!found_profile) { OPENVINO_THROW("Cannot find operator by node type: ", node_type_name); } - time += found_profile->real_time.count(); } } From 94903a4f5b19b8343bd8a8b2fcaee1c2e543a408 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Wed, 22 Jul 2026 14:26:45 +0000 Subject: [PATCH 084/121] [CI] Changed GC_TAG and OV_GPU_QUEUE_TYPE --- .github/workflows/graph-compiler.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/graph-compiler.yml b/.github/workflows/graph-compiler.yml index 3706aebb3e0f94..9f0526085cbb9b 100644 --- a/.github/workflows/graph-compiler.yml +++ b/.github/workflows/graph-compiler.yml @@ -9,7 +9,7 @@ permissions: env: GC_REPO: https://x-access-token:${{ secrets.GC_TOKEN }}@github.com/intel-sandbox/graph-compiler - GC_TAG: ap/ocl-events + GC_TAG: main BUILD_DIR: ${{ github.workspace }}-gc-build OUTPUT_DIR: ${{ github.workspace }}-gc-bin @@ -59,6 +59,7 @@ jobs: exclude='.*ScaledAttnLayerGPUMlirTest.CompareWithRefs.*|mlir_Transpose.*|mlir_ReshapeAndTranspose.*' export OV_MLIR=1 + export OV_GPU_QUEUE_TYPE=out-of-order func_tests="$OUTPUT_DIR/bin/intel64/Release/ov_gpu_func_tests" filter=$(printf '%s:' \ 'MLIRExecution.SimpleMatmulf16' \ From 93fa790a7daaac150edce35a6edadab3a3f15bca Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Wed, 22 Jul 2026 17:34:32 +0200 Subject: [PATCH 085/121] Move mlir-properties to GPU plugin (#7) Signed-off-by: dchigarev --- .../openvino/runtime/internal_properties.hpp | 27 ----------------- .../src/graph/impls/common/mlir_primitive.cpp | 2 +- .../plugin/transformations/mlir/mlir_op.cpp | 2 +- .../transformations/mlir/properties.hpp | 29 +++++++++++++++++++ 4 files changed, 31 insertions(+), 29 deletions(-) create mode 100644 src/plugins/intel_gpu/src/plugin/transformations/mlir/properties.hpp diff --git a/src/inference/dev_api/openvino/runtime/internal_properties.hpp b/src/inference/dev_api/openvino/runtime/internal_properties.hpp index 404a30e3f082c2..a7a92bd869bce9 100644 --- a/src/inference/dev_api/openvino/runtime/internal_properties.hpp +++ b/src/inference/dev_api/openvino/runtime/internal_properties.hpp @@ -174,33 +174,6 @@ static constexpr Property key_cache_quan static constexpr Property value_cache_quant_mode{"VALUE_CACHE_QUANT_MODE"}; -/* -* @brief Namespace for properties related to MLIR operations within the GPU plugin. - * These properties are used as evaluation context parameters for MLIR operations, - * assisting in managing events, result tracking, and kernel argument types. - */ -namespace mlir_meta { - -/** - * @brief This key identifies a list of cl_event to wait for a kernel execution. - * @ingroup ov_dev_api_plugin_mlir_meta_api - */ -static constexpr Property> wait_list{"EVENTS_WAIT_LIST"}; - -/** - * @brief This key identifies a pointer to a list that should be filled with - * result cl_events of a kernel execution. - * @ingroup ov_dev_api_plugin_mlir_meta_api - */ -static constexpr Property*> result_events{"RESULT_EVENTS"}; - -/** - * @brief This key identifies whether the kernel argument at [i] position is USM pointer - * @ingroup ov_dev_api_plugin_mlir_meta_api - */ -static constexpr Property> is_kernel_arg_usm{"IS_KERNEL_ARG_USM"}; - -} // namespace mlir_meta /** * @brief KV cache quantization algorithm. * Selects SCALAR vs TURBO for integer cache precision (u8/u4); defaults to SCALAR when unset. diff --git a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp index 9ee165d8dc271c..34aa73f0b080f6 100644 --- a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp +++ b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp @@ -14,7 +14,7 @@ #include "openvino/core/node.hpp" #include "openvino/runtime/intel_gpu/ocl/ocl_wrapper.hpp" #include "openvino/runtime/intel_gpu/remote_properties.hpp" -#include "openvino/runtime/internal_properties.hpp" +#include "plugin/transformations/mlir/properties.hpp" #include "register.hpp" #include "registry/implementation_map.hpp" diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp index 099e95189dd286..f9a6fd4da7b749 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp @@ -29,7 +29,7 @@ #include "gc/Utils/Error.h" #include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" #include "openvino/runtime/intel_gpu/remote_properties.hpp" -#include "openvino/runtime/internal_properties.hpp" +#include "properties.hpp" namespace { diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/properties.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/properties.hpp new file mode 100644 index 00000000000000..a9a273a21916a9 --- /dev/null +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/properties.hpp @@ -0,0 +1,29 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "openvino/runtime/properties.hpp" + +namespace ov::internal::mlir_meta { + +/** + * @brief This key identifies a list of cl_event to wait for a kernel execution. + */ +static constexpr Property> wait_list{"EVENTS_WAIT_LIST"}; + +/** + * @brief This key identifies a pointer to a list that should be filled with + * result cl_events of a kernel execution. + */ +static constexpr Property*> result_events{"RESULT_EVENTS"}; + +/** + * @brief This key identifies whether the kernel argument at [i] position is USM pointer + */ +static constexpr Property> is_kernel_arg_usm{"IS_KERNEL_ARG_USM"}; + +} // namespace ov::internal::mlir_meta From 0685a31ac27b1e19761a939c10b3dd458607ee9b Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Thu, 23 Jul 2026 19:33:54 +0200 Subject: [PATCH 086/121] Restructure MLIR integration in GPU plugin (#8) * stage-1 Signed-off-by: dchigarev * stage-2 Signed-off-by: dchigarev * stage-3 Signed-off-by: dchigarev * stage-4 Signed-off-by: dchigarev * stage-5 Signed-off-by: dchigarev * stage-6 Signed-off-by: dchigarev * stage-7(revert remove-item) Signed-off-by: dchigarev * stage-8(revert destructor) Signed-off-by: dchigarev * stage-9(remove unnecessary includes) Signed-off-by: dchigarev * stage-10(fix non-gc-build) Signed-off-by: dchigarev * stage-11(cleanup) Signed-off-by: dchigarev * stage-12(evaluate-base) Signed-off-by: dchigarev * stage-13(mlir/interface) Signed-off-by: dchigarev * stage-14(namespaces) Signed-off-by: dchigarev --------- Signed-off-by: dchigarev --- .github/workflows/graph-compiler.yml | 2 +- .../openvino/runtime/intel_gpu/properties.hpp | 12 + src/plugins/intel_gpu/CMakeLists.txt | 21 +- .../include/intel_gpu/op/mlir_op.hpp | 55 +++ .../intel_gpu/primitives/mlir_primitive.hpp | 4 +- .../include/intel_gpu/runtime/options.inl | 1 + .../include/transformations/mlir/convert.hpp | 36 -- .../src/graph/impls/common/mlir_primitive.cpp | 2 +- .../intel_gpu/src/plugin/ops/mlir_op.cpp | 16 +- .../transformations/mlir/common/README.md | 6 +- .../mlir/common/conversion_context.cpp | 6 +- .../mlir/common/conversion_context.hpp | 6 +- .../mlir/common/convert_common.cpp | 6 +- .../mlir/common/convert_common.hpp | 10 +- .../mlir/common/converters/binary_eltwise.hpp | 8 +- .../mlir/common/converters/concat.hpp | 6 +- .../mlir/common/converters/floor.hpp | 6 +- .../mlir/common/converters/gather.hpp | 6 +- .../mlir/common/converters/matmul.hpp | 6 +- .../mlir/common/converters/reduce.hpp | 6 +- .../mlir/common/converters/relu.hpp | 6 +- .../mlir/common/converters/reshape.hpp | 6 +- .../mlir/common/converters/sdpa.hpp | 6 +- .../mlir/common/converters/shape_of.hpp | 6 +- .../mlir/common/converters/slice.hpp | 6 +- .../mlir/common/converters/squeeze.hpp | 6 +- .../mlir/common/converters/transpose.hpp | 6 +- .../mlir/common/converters/unary_eltwise.hpp | 6 +- .../mlir/common/converters/unsqueeze.hpp | 6 +- .../transformations/mlir/common/typedefs.hpp | 7 +- .../mlir/conversion/patterns.cpp | 6 +- .../mlir/conversion/patterns.hpp | 6 +- .../plugin/transformations/mlir/convert.cpp | 25 +- .../transformations/mlir/graph_converter.cpp | 6 +- .../transformations/mlir/graph_converter.hpp | 6 +- .../mlir/interface/convert.hpp | 17 + .../mlir/interface/mlir_evaluate_base.hpp | 25 ++ .../mlir/{ => interface}/properties.hpp | 0 .../transformations/mlir/mlir_evaluate.cpp | 155 ++++++++ .../transformations/mlir/mlir_evaluate.hpp | 45 +++ .../plugin/transformations/mlir/mlir_op.cpp | 374 ------------------ .../plugin/transformations/mlir/mlir_op.hpp | 74 ---- .../transformations/mlir/subgraph_tracker.cpp | 6 +- .../transformations/mlir/subgraph_tracker.hpp | 6 +- .../src/plugin/transformations/op/mlir_op.cpp | 194 +++++++++ .../src/plugin/transformations_pipeline.cpp | 29 +- .../mlir_op/matmul_rms_norm_concat.cpp | 13 +- .../functional/single_layer_tests/reduce.cpp | 13 +- 48 files changed, 631 insertions(+), 651 deletions(-) create mode 100644 src/plugins/intel_gpu/include/intel_gpu/op/mlir_op.hpp delete mode 100644 src/plugins/intel_gpu/include/transformations/mlir/convert.hpp create mode 100644 src/plugins/intel_gpu/src/plugin/transformations/mlir/interface/convert.hpp create mode 100644 src/plugins/intel_gpu/src/plugin/transformations/mlir/interface/mlir_evaluate_base.hpp rename src/plugins/intel_gpu/src/plugin/transformations/mlir/{ => interface}/properties.hpp (100%) create mode 100644 src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp create mode 100644 src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.hpp delete mode 100644 src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp delete mode 100644 src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.hpp create mode 100644 src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp diff --git a/.github/workflows/graph-compiler.yml b/.github/workflows/graph-compiler.yml index 9f0526085cbb9b..56d17f9175b1ed 100644 --- a/.github/workflows/graph-compiler.yml +++ b/.github/workflows/graph-compiler.yml @@ -58,7 +58,7 @@ jobs: # https://github.com/llvm/llvm-project/pull/197281 exclude='.*ScaledAttnLayerGPUMlirTest.CompareWithRefs.*|mlir_Transpose.*|mlir_ReshapeAndTranspose.*' - export OV_MLIR=1 + export OV_GPU_ENABLE_MLIR=1 export OV_GPU_QUEUE_TYPE=out-of-order func_tests="$OUTPUT_DIR/bin/intel64/Release/ov_gpu_func_tests" filter=$(printf '%s:' \ diff --git a/src/inference/include/openvino/runtime/intel_gpu/properties.hpp b/src/inference/include/openvino/runtime/intel_gpu/properties.hpp index 3ac92e47ef6718..207ae5e6dff257 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/properties.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/properties.hpp @@ -77,6 +77,18 @@ static constexpr Property enable_loop_unrolling{"GPU_ENABLE_LOOP_UNROLLING */ static constexpr Property disable_winograd_convolution{"GPU_DISABLE_WINOGRAD_CONVOLUTION"}; +/** + * @brief Enables MLIR-based Graph Compiler execution for supported subgraphs. + * When on, matching subgraphs (matmul, elementwise, SDPA, reduction, etc.) + * are compiled through the MLIR/Graph-Compiler pipeline and executed as a + * single fused GPU kernel via cldnn::mlir_primitive. + * Requires the plugin to be built with -DENABLE_GRAPH_COMPILER=ON; setting + * this to true on a plugin built without Graph Compiler support raises an + * exception at compile_model() time. + * @ingroup ov_runtime_ocl_gpu_prop_cpp_api + */ +static constexpr Property enable_mlir{"GPU_ENABLE_MLIR"}; + namespace hint { /** * @brief This enum represents the possible value of ov::intel_gpu::hint::queue_throttle property: diff --git a/src/plugins/intel_gpu/CMakeLists.txt b/src/plugins/intel_gpu/CMakeLists.txt index 43b13a6d443a13..d0b93720c0ea0e 100644 --- a/src/plugins/intel_gpu/CMakeLists.txt +++ b/src/plugins/intel_gpu/CMakeLists.txt @@ -134,12 +134,17 @@ add_subdirectory(src/graph) file(GLOB_RECURSE PLUGIN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/plugin/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/include/intel_gpu/plugin/*.hpp) +# MLIR sources are always excluded from the main plugin target. When +# ENABLE_GRAPH_COMPILER=ON they are built into a dedicated OBJECT library +# (openvino_intel_gpu_mlir_obj) that has MLIR/Graph-Compiler include paths; +# when OFF they must not be compiled at all. +set(OV_GPU_MLIR_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/plugin/transformations/mlir) +file(GLOB_RECURSE GPU_MLIR_SOURCES ${OV_GPU_MLIR_DIR}/*.cpp) +list(REMOVE_ITEM PLUGIN_SOURCES ${GPU_MLIR_SOURCES}) + if(ENABLE_GRAPH_COMPILER) include(${CMAKE_SOURCE_DIR}/cmake/graph-compiler.cmake) - set(OV_GPU_MLIR_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/plugin/transformations/mlir) - file(GLOB_RECURSE GPU_MLIR_SOURCES ${OV_GPU_MLIR_DIR}/*.cpp) - list(REMOVE_ITEM PLUGIN_SOURCES ${GPU_MLIR_SOURCES}) set(mlir_lib openvino_intel_gpu_mlir_obj) add_library(${mlir_lib} OBJECT ${GPU_MLIR_SOURCES}) target_compile_options(${mlir_lib} PRIVATE -Wno-error) @@ -147,9 +152,6 @@ if(ENABLE_GRAPH_COMPILER) target_link_libraries(${mlir_lib} PRIVATE openvino::runtime openvino_intel_gpu_graph GraphCompiler) target_include_directories(${mlir_lib} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/ - # FIXME: remove when ov::MLIROp definition is moved to a common plugin header - ${CMAKE_CURRENT_SOURCE_DIR}/src/plugin - ${OpenVINO_SOURCE_DIR}/src/common/transformations/include ${OV_GPU_MLIR_DIR}) set(GPU_PLUGIN_OBJECT_LIBRARIES ${mlir_lib}) @@ -175,12 +177,7 @@ target_include_directories(${TARGET_NAME} PRIVATE if(ENABLE_GRAPH_COMPILER) target_link_libraries(${TARGET_NAME} PRIVATE GraphCompiler) - target_compile_definitions(${TARGET_NAME} PUBLIC GRAPH_COMPILER) - target_include_directories(${TARGET_NAME} PRIVATE - # FIXME: remove when ov::MLIROp definition is moved to a common plugin header - ${CMAKE_CURRENT_SOURCE_DIR}/src/plugin - ${OpenVINO_SOURCE_DIR}/src/common/transformations/include - ${OV_GPU_MLIR_DIR}) + target_compile_definitions(${TARGET_NAME} PRIVATE GRAPH_COMPILER) endif() ov_set_threading_interface_for(${TARGET_NAME}) diff --git a/src/plugins/intel_gpu/include/intel_gpu/op/mlir_op.hpp b/src/plugins/intel_gpu/include/intel_gpu/op/mlir_op.hpp new file mode 100644 index 00000000000000..3f5b5a03d0ecd7 --- /dev/null +++ b/src/plugins/intel_gpu/include/intel_gpu/op/mlir_op.hpp @@ -0,0 +1,55 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include + +#include "openvino/core/any.hpp" +#include "openvino/core/partial_shape.hpp" +#include "openvino/core/type/element_type.hpp" +#include "openvino/op/op.hpp" +#include "openvino/runtime/tensor.hpp" + +namespace ov::intel_gpu { +namespace mlir { +class MLIREvaluateBase; +} // namespace mlir +namespace op { + +using OVOutputTypes = std::vector>; + +// Maps [output index][dimension index] -> [input index][dimension index] to +// infer shapes for the entire subgraph. +using DimensionsMap = std::vector>>; + +class MLIROp : public ov::op::Op { + std::shared_ptr engine; + OVOutputTypes output_types; + DimensionsMap dimensions_map; + +public: + OPENVINO_OP("MLIROp"); + + MLIROp() = default; + + MLIROp(const ov::OutputVector& args, + std::shared_ptr engine, + const OVOutputTypes& output_types, + const DimensionsMap& dimensions_map); + + void validate_and_infer_types() override; + std::shared_ptr clone_with_new_inputs(const ov::OutputVector& new_args) const override; + bool evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const override; + bool evaluate(ov::TensorVector& outputs, + const ov::TensorVector& inputs, + const ov::EvaluationContext& evaluationContext) const override; + bool has_evaluate() const override; + std::vector shape_infer(const std::vector& input_shapes) const; +}; + +} // namespace op +} // namespace ov::intel_gpu diff --git a/src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp b/src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp index 8ffdf78d21e9ce..5ce37f9a5037c0 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp @@ -12,12 +12,12 @@ #include "primitive.hpp" namespace ov { -class Node; // forward-decl — the underlying op is ov::mlir::MLIROp +class Node; // forward-decl — the underlying op is ov::intel_gpu::op::MLIROp } namespace cldnn { -/// @brief Primitive that wraps an ov::mlir::MLIROp node. Its execute_impl +/// @brief Primitive that wraps an ov::intel_gpu::op::MLIROp node. Its execute_impl /// (see impls/common/mlir_primitive.cpp) forwards to MLIROp::evaluate(). struct mlir_primitive : public primitive_base { CLDNN_DECLARE_PRIMITIVE(mlir_primitive) diff --git a/src/plugins/intel_gpu/include/intel_gpu/runtime/options.inl b/src/plugins/intel_gpu/include/intel_gpu/runtime/options.inl index 931faecf4d10f2..c12797faa37e1f 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/runtime/options.inl +++ b/src/plugins/intel_gpu/include/intel_gpu/runtime/options.inl @@ -20,6 +20,7 @@ OV_CONFIG_RELEASE_OPTION(ov::intel_gpu::hint, queue_throttle, ov::intel_gpu::hin OV_CONFIG_RELEASE_OPTION(ov::intel_gpu::hint, queue_priority, ov::hint::Priority::MEDIUM, "Low-level hint that controls queue priority property") OV_CONFIG_RELEASE_OPTION(ov::intel_gpu::hint, enable_sdpa_optimization, true, "Enable/Disable fused SDPA primitive execution") OV_CONFIG_RELEASE_OPTION(ov::intel_gpu::hint, enable_lora_operation, true, "Enable/Disable LoRA operation. The separate operation is less versatile, but has better performance") +OV_CONFIG_RELEASE_OPTION(ov::intel_gpu, enable_mlir, false, "Enable/Disable MLIR/Graph-Compiler execution for supported subgraphs. Requires ENABLE_GRAPH_COMPILER=ON at build time") OV_CONFIG_RELEASE_OPTION(ov::intel_gpu::hint, enable_large_allocations, false, "Allow buffer allocations that exceed the device max allocation size. Enabling this option may lead to performance degradation") OV_CONFIG_RELEASE_OPTION(ov::intel_gpu, enable_loop_unrolling, true, "Enable/Disable Loop/TensorIterator operation unrolling") OV_CONFIG_RELEASE_OPTION(ov::intel_gpu, disable_winograd_convolution, false, "Enable/Disable winograd convolution implementation if available") diff --git a/src/plugins/intel_gpu/include/transformations/mlir/convert.hpp b/src/plugins/intel_gpu/include/transformations/mlir/convert.hpp deleted file mode 100644 index 5c40b766ffdb68..00000000000000 --- a/src/plugins/intel_gpu/include/transformations/mlir/convert.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "openvino/core/core_visibility.hpp" -#include "openvino/core/model.hpp" -#include "openvino/core/partial_shape.hpp" -#include "openvino/util/env_util.hpp" - -namespace ov { - -namespace pass { - -inline bool is_mlir_transform_enabled() { - return util::getenv_bool("OV_MLIR", false); -} - -OPENVINO_API void transformMLIR(std::shared_ptr model, - std::shared_ptr loweringContext); - -} - -namespace mlir { - -// Resolves dynamic dims of MLIROp output shapes from runtime input shapes via -// the op's dimensions_map. Asserts if the node is not an MLIROp. -// Exposed here so callers outside transformations (e.g. Intel GPU plugin) can -// invoke shape inference without depending on the private MLIROp header. -OPENVINO_API std::vector mlir_op_shape_infer( - const std::shared_ptr& op, - const std::vector& input_shapes); - -} -} diff --git a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp index 34aa73f0b080f6..aa8920317c4b5a 100644 --- a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp +++ b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp @@ -14,7 +14,7 @@ #include "openvino/core/node.hpp" #include "openvino/runtime/intel_gpu/ocl/ocl_wrapper.hpp" #include "openvino/runtime/intel_gpu/remote_properties.hpp" -#include "plugin/transformations/mlir/properties.hpp" +#include "plugin/transformations/mlir/interface/properties.hpp" #include "register.hpp" #include "registry/implementation_map.hpp" diff --git a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp index e67d28a75a062d..7f5c41c01717a1 100644 --- a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp +++ b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp @@ -1,22 +1,16 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // #ifdef GRAPH_COMPILER +#include "intel_gpu/op/mlir_op.hpp" #include "intel_gpu/plugin/common_utils.hpp" #include "intel_gpu/plugin/program_builder.hpp" #include "intel_gpu/primitives/mlir_primitive.hpp" -#include "transformations/mlir/convert.hpp" // ov::mlir::mlir_op_shape_infer -#include "transformations/mlir/mlir_op.hpp" - -// REGISTER_FACTORY_IMPL(internal, MLIR) expands to: -// * RegisterFactory (requires the alias below) -// * a call to Create##MLIR##Op == CreateMLIROp -// We use op_name = "MLIR" (not "MLIROp") to keep the "Op" suffix that the -// macro concatenates and match the Gemm/KVCache/etc. naming convention. + namespace ov::op::internal { -using MLIR = ov::mlir::MLIROp; +using MLIR = ov::intel_gpu::op::MLIROp; } // namespace ov::op::internal namespace ov::intel_gpu { @@ -28,7 +22,7 @@ static void CreateMLIROp(ProgramBuilder& p, const std::shared_ptr& input_shapes) { - return ov::mlir::mlir_op_shape_infer(op, input_shapes); + return op->shape_infer(input_shapes); }; cldnn::mlir_primitive primitive(layer_name, diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/README.md b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/README.md index e65de8cd6e0dcd..4d226f3a3d87fa 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/README.md +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/README.md @@ -9,12 +9,12 @@ This folder contains converters and helper classes to convert certain OV operati Contains `.hpp` files, each representing a converter for a specific OV operation or a class of operations (matmul, reduction, binary-elementwise). A converter must implement the following interface — it takes a conversion context and an OV node, produces a sequence of MLIR operations, and returns the final op: ```c++ -mlir::Operation* operator()(ov::mlir::ConversionContext& context, std::shared_ptr node) +mlir::Operation* operator()(ov::intel_gpu::mlir::ConversionContext& context, std::shared_ptr node) ``` ### `conversion_context` -`ov::mlir::ConversionContext` is a class that provides converters with: +`ov::intel_gpu::mlir::ConversionContext` is a class that provides converters with: - `mlir::Context` - `mlir::OpBuilder` - Mapping between `ov::Node` inputs and MLIR tensors @@ -42,7 +42,7 @@ Example: ```c++ class OvGraphImporter { - ov::mlir::ConversionContext _ctx; + ov::intel_gpu::mlir::ConversionContext _ctx; ov::Model _model; mlir::ModuleOp _module; ... diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.cpp index 62a4c1f9d2e5ac..d74c079cedaf1a 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.cpp @@ -8,8 +8,7 @@ #include "conversion_context.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { using namespace ::mlir; @@ -34,5 +33,4 @@ SmallVector ConversionContext::get_dynamic_dimension_values (const Partia } -} // namespace mlir -} // namespace ov \ No newline at end of file +} // namespace ov::intel_gpu::mlir \ No newline at end of file diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.hpp index 73e1655eac1933..c054df92d94bbc 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.hpp @@ -13,8 +13,7 @@ #include "typedefs.hpp" #include "convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { using ::mlir::Value; using ::mlir::MLIRContext; @@ -46,5 +45,4 @@ class ConversionContext { SmallVector get_dynamic_dimension_values(const PartialShape& shape); }; -} // namespace mlir -} // namespace ov \ No newline at end of file +} // namespace ov::intel_gpu::mlir \ No newline at end of file diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp index 3d7581ce0bbcef..056bee530a4a02 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp @@ -6,8 +6,7 @@ #include -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { Location createLayerLocation(MLIRContext* ctx, const std::string& layerName, const std::string& layerType) { const auto layerNameAttr = StringAttr::get(ctx, layerName); @@ -203,5 +202,4 @@ bool is_debug() { return util::getenv_bool("OV_MLIR_DEBUG", false); } -} // namespace mlir -} // namespace ov \ No newline at end of file +} // namespace ov::intel_gpu::mlir \ No newline at end of file diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp index 8936e28181d6a0..ad28e7670c163a 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp @@ -18,15 +18,14 @@ #include "typedefs.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { using namespace ::mlir; bool is_debug(); -#define OPENVINO_MLIR_DEBUG(X) do if(::ov::mlir::is_debug()) { X; } while(false) -#define OPENVINO_MLIR_DEBUG_PRINT(X) do if(::ov::mlir::is_debug()) { ::std::cerr << X; } while(false) +#define OPENVINO_MLIR_DEBUG(X) do if(::ov::intel_gpu::mlir::is_debug()) { X; } while(false) +#define OPENVINO_MLIR_DEBUG_PRINT(X) do if(::ov::intel_gpu::mlir::is_debug()) { ::std::cerr << X; } while(false) Location createLayerLocation(MLIRContext* ctx, const std::string& layerName, const std::string& layerType); @@ -96,5 +95,4 @@ bool has_broadcast(Dimension from, Dimension to); bool statically_broadcastable(const PartialShape& from, const PartialShape& to); -} // namespace mlir -} // namespace ov \ No newline at end of file +} // namespace ov::intel_gpu::mlir \ No newline at end of file diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp index 54f9da32f1aad1..d309b8712a22f0 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp @@ -13,11 +13,10 @@ #include #include "openvino/pass/pattern/op/wrap_type.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { using namespace ov; -using namespace ov::mlir; +using namespace ov::intel_gpu::mlir; using ::mlir::ValueRange; template @@ -55,5 +54,4 @@ struct ConvertBinaryEltwise { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp index ba32e3e1274872..6b5ae999becb8a 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp @@ -13,8 +13,7 @@ #include "../convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertConcat { Operation* operator()(ConversionContext& context, NodePtr node) { @@ -37,6 +36,5 @@ struct ConvertConcat { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/floor.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/floor.hpp index 6ed7a7a88e3f6a..2d45d8b9921778 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/floor.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/floor.hpp @@ -13,8 +13,7 @@ #include "../convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertFloor { Operation* operator()(ConversionContext& context, NodePtr node) { @@ -31,5 +30,4 @@ struct ConvertFloor { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp index d4491f4eea5a40..6093a586758756 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp @@ -14,8 +14,7 @@ #include "../convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertGather { Operation* operator()(ConversionContext& context, NodePtr node) { @@ -72,6 +71,5 @@ struct ConvertGather { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp index e00b40acef2810..8b5aa3185446eb 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp @@ -13,8 +13,7 @@ #include "../convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertMatMul { Operation* operator()(ConversionContext& context, NodePtr node) { @@ -111,5 +110,4 @@ struct ConvertMatMul { } }; -} // namespace mlir -} // namespace ov \ No newline at end of file +} // namespace ov::intel_gpu::mlir \ No newline at end of file diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp index a4b14de939e088..7baad6ce64d140 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp @@ -22,8 +22,7 @@ #include "mlir/IR/Value.h" #include "openvino/pass/pattern/op/wrap_type.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { using Value = ::mlir::Value; using ValueRange = ::mlir::ValueRange; @@ -173,5 +172,4 @@ struct ConvertReduce { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp index 1e9d4c9b8c8463..9d591fd26ced21 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp @@ -12,8 +12,7 @@ #include "../convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertRelu { Operation* operator()(ConversionContext& context, NodePtr node) { @@ -33,5 +32,4 @@ struct ConvertRelu { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp index 5ff856f34be20a..cc105980565884 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp @@ -10,8 +10,7 @@ #include "mlir/Dialect/Tensor/IR/Tensor.h" #include "openvino/pass/pattern/op/wrap_type.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertReshape { Operation* operator()(ConversionContext& context, NodePtr node) { @@ -61,5 +60,4 @@ struct ConvertReshape { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/sdpa.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/sdpa.hpp index ede3d72d8d9934..63d6ceaac9e530 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/sdpa.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/sdpa.hpp @@ -21,8 +21,7 @@ #include "../convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertSDPA { static SmallVector getStandardAttentionIndexingMaps(MLIRContext *ctx, @@ -159,6 +158,5 @@ struct ConvertSDPA { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/shape_of.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/shape_of.hpp index cafbba38edb57d..0f5d5a27a7fc0e 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/shape_of.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/shape_of.hpp @@ -15,8 +15,7 @@ #include "../convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertShapeOf { Operation* operator()(ConversionContext& context, NodePtr node) { @@ -32,5 +31,4 @@ struct ConvertShapeOf { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp index ece0f08e0e4f40..4719b1988031f5 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp @@ -12,8 +12,7 @@ #include "../convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertSlice { Operation* operator()(ConversionContext& context, NodePtr node) { @@ -39,5 +38,4 @@ struct ConvertSlice { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp index b2880da804e7ee..be1c374d6ef3f7 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp @@ -13,8 +13,7 @@ #include "../convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertSqueeze { Operation* operator()(ConversionContext& context, NodePtr node) { @@ -42,5 +41,4 @@ struct ConvertSqueeze { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/transpose.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/transpose.hpp index ce031922f6d178..245087cf7bac06 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/transpose.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/transpose.hpp @@ -12,8 +12,7 @@ #include "../convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertTranspose { Operation* operator()(ConversionContext& context, NodePtr node) { @@ -39,6 +38,5 @@ struct ConvertTranspose { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unary_eltwise.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unary_eltwise.hpp index 9745a1c752499b..6827d19908ca98 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unary_eltwise.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unary_eltwise.hpp @@ -8,8 +8,7 @@ #include "mlir/Dialect/Linalg/Passes.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { template struct ConvertUnaryEltwise { @@ -26,5 +25,4 @@ struct ConvertUnaryEltwise { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unsqueeze.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unsqueeze.hpp index 5024a3c61bc5f8..0b2ffc28a25c86 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unsqueeze.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unsqueeze.hpp @@ -15,8 +15,7 @@ #include "../convert_common.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct ConvertUnsqueeze { Operation* operator()(ConversionContext& context, NodePtr node) { @@ -60,6 +59,5 @@ struct ConvertUnsqueeze { } }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/typedefs.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/typedefs.hpp index a773b9aa76884b..697684d37f1a93 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/typedefs.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/typedefs.hpp @@ -8,13 +8,10 @@ #include "openvino/core/symbol.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { using NodePtr = std::shared_ptr; using SymbolPtr = std::shared_ptr; -using OVOutputTypes = std::vector>; using InputVector = std::vector>; -} // namespace mlir -} // namespace ov \ No newline at end of file +} // namespace ov::intel_gpu::mlir \ No newline at end of file diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp index 26ceb7b6beacdd..a898fbaa584273 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp @@ -51,8 +51,7 @@ #include "../common/converters/unsqueeze.hpp" #include "../common/converters/binary_eltwise.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { using namespace ov::pass::pattern; using namespace ov::op; @@ -154,5 +153,4 @@ template class UnaryEltwisePattern; template class UnaryEltwisePattern; template class UnaryEltwisePattern; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp index 2f0d70f9769f5f..89c1d989cd186e 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp @@ -10,8 +10,7 @@ // #include // #include -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { class ReluPattern : public MarkPattern { public: @@ -114,5 +113,4 @@ class UnaryEltwisePattern : public MarkPattern { UnaryEltwisePattern(); }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp index b955dbcf45b126..87c5af37c1288a 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "transformations/mlir/convert.hpp" +#include "interface/convert.hpp" #include #include @@ -76,7 +76,8 @@ #include "gc/Transforms/Passes.h" -#include "mlir_op.hpp" +#include "intel_gpu/op/mlir_op.hpp" +#include "mlir_evaluate.hpp" #include "conversion/patterns.hpp" #include "openvino/core/dimension.hpp" #include "openvino/core/rt_info.hpp" @@ -88,7 +89,7 @@ namespace { using namespace mlir; -using namespace ov::mlir; +using namespace ov::intel_gpu::mlir; MemRefType convertTensorToMemRef(TensorType tensorType) { ArrayRef shape = tensorType.getShape(); @@ -256,7 +257,7 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, for (size_t idx : keptInputIndices) { inputs.push_back(subgraph->inputs[idx]); } - using Index = DimensionsMap::value_type::value_type; + using Index = ov::intel_gpu::op::DimensionsMap::value_type::value_type; std::map input_map; for (size_t i = 0; i < inputs.size(); ++i) { auto input = inputs[i]; @@ -278,15 +279,15 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, std::tuple empty(-1, -1); const auto& outputs = subgraph->outputs; - OVOutputTypes output_types; - DimensionsMap output_map; + ov::intel_gpu::op::OVOutputTypes output_types; + ov::intel_gpu::op::DimensionsMap output_map; output_map.reserve(outputs.size()); for (size_t i = 0; i < outputs.size(); ++i) { auto output = outputs[i]; auto shape = output.get_partial_shape(); output_types.push_back( std::make_tuple(output.get_element_type(), shape)); - DimensionsMap::value_type dm; + ov::intel_gpu::op::DimensionsMap::value_type dm; dm.reserve(shape.size()); for (size_t j = 0; j < shape.size(); ++j) { auto dim = shape[j]; @@ -296,7 +297,7 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, } output_map.emplace_back(dm); } - return std::make_shared( + return std::make_shared( inputs, std::make_shared(std::move(module), loweringContext), output_types, @@ -402,9 +403,7 @@ MLIRContext* get_shared_mlir_context() { } // namespace -void ov::pass::transformMLIR(std::shared_ptr model, - std::shared_ptr loweringContext) { - if (is_mlir_transform_enabled()) { - injectMLIR(model, get_shared_mlir_context(), loweringContext); - } +void ov::intel_gpu::mlir::transformMLIR(std::shared_ptr model, + std::shared_ptr loweringContext) { + injectMLIR(model, get_shared_mlir_context(), loweringContext); } diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp index 94d379ac8c9cdc..de5b47ec8735db 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp @@ -9,8 +9,7 @@ #include "graph_converter.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { using namespace ::mlir; @@ -106,5 +105,4 @@ MarkPattern::MarkPattern(NodePtr pattern, MarkPattern::Callback callback) { } -} // namespace mlir -} // namespace ov \ No newline at end of file +} // namespace ov::intel_gpu::mlir \ No newline at end of file diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp index 6550870a9dc53b..4ad5ce9b984017 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp @@ -14,8 +14,7 @@ #include "common/convert_common.hpp" #include "common/conversion_context.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { using ::mlir::MLIRContext; using ::mlir::OpBuilder; @@ -67,5 +66,4 @@ class MarkPattern : public ov::pass::MatcherPass { MarkPattern(NodePtr pattern, Callback callback); }; -} // namespace mlir -} // namespace ov \ No newline at end of file +} // namespace ov::intel_gpu::mlir \ No newline at end of file diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/interface/convert.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/interface/convert.hpp new file mode 100644 index 00000000000000..157bda93fba550 --- /dev/null +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/interface/convert.hpp @@ -0,0 +1,17 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "openvino/core/any.hpp" +#include "openvino/core/model.hpp" + +namespace ov::intel_gpu::mlir { + +void transformMLIR(std::shared_ptr model, + std::shared_ptr loweringContext); + +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/interface/mlir_evaluate_base.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/interface/mlir_evaluate_base.hpp new file mode 100644 index 00000000000000..2d7841081818a9 --- /dev/null +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/interface/mlir_evaluate_base.hpp @@ -0,0 +1,25 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "openvino/core/node.hpp" +#include "openvino/runtime/tensor.hpp" + +namespace ov::intel_gpu::mlir { + +class MLIREvaluateBase { +public: + virtual bool requires_packed_args() const = 0; + virtual bool invoke(const ov::TensorVector& inputs, + ov::TensorVector& outputs, + const ov::EvaluationContext& evaluationContext) = 0; + virtual bool invoke_packed(std::vector& args, + const ov::EvaluationContext& evaluationContext) = 0; + virtual ~MLIREvaluateBase() = default; +}; + +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/properties.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/interface/properties.hpp similarity index 100% rename from src/plugins/intel_gpu/src/plugin/transformations/mlir/properties.hpp rename to src/plugins/intel_gpu/src/plugin/transformations/mlir/interface/properties.hpp diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp new file mode 100644 index 00000000000000..7c4e1a3c19a873 --- /dev/null +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp @@ -0,0 +1,155 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "mlir_evaluate.hpp" + +#include +#include +#include + +#include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" +#include "gc/Transforms/Passes.h" +#include "gc/Utils/Error.h" +#include "mlir/Dialect/Bufferization/Transforms/Passes.h" +#include "mlir/Pass/PassManager.h" +#include "openvino/runtime/intel_gpu/remote_properties.hpp" +#include "interface/properties.hpp" + +namespace ov::intel_gpu::mlir { + +using namespace ::mlir; + +static cl_device_id extract_device_from_context(cl_context context) { + size_t devices_size; + cl_int err = clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &devices_size); + if (err != CL_SUCCESS) { + OPENVINO_THROW("Error getting context info: ", err); + } + if (devices_size / sizeof(cl_device_id) != 1) { + OPENVINO_THROW("Expected exactly one device in the context, got ", devices_size); + } + + cl_device_id devices; + err = clGetContextInfo(context, CL_CONTEXT_DEVICES, devices_size, &devices, NULL); + if (err != CL_SUCCESS) { + OPENVINO_THROW("Error getting device IDs: ", err); + } + + return devices; +} + +MLIREvaluateGcGPU::MLIREvaluateGcGPU(OwningOpRef<::mlir::ModuleOp> _module, + std::shared_ptr loweringContext) { + gc::gpu::OclModuleBuilderOpts opts; + gc::gpu::OclModuleBuilder builder(std::move(_module), opts); + + auto it = loweringContext->find(ov::intel_gpu::ocl_context.name()); + if (it == loweringContext->end()) { + OPENVINO_THROW("No cl_context provided for OpenCL execution"); + } + auto context = reinterpret_cast(it->second.as()); + // assuming there's always one device per context + auto device = extract_device_from_context(context); + + if (auto mod = builder.build(device, context)) { + module = *mod; + } else { + OPENVINO_THROW("Failed to build gc::gpuOclModule module"); + } +} + +bool MLIREvaluateGcGPU::invoke(const ov::TensorVector& inputs, + ov::TensorVector& outputs, + const ov::EvaluationContext& evaluationContext) { + std::vector waitList; + gc::gpu::OclContext ctx = build_ocl_context(evaluationContext, waitList); + gc::gpu::StaticExecutor exec(module); + + auto it = evaluationContext.find(ov::internal::mlir_meta::is_kernel_arg_usm.name()); + if (it == evaluationContext.end()) { + OPENVINO_THROW("No is_kernel_arg_usm provided for OpenCL execution"); + } + std::vector arg_types = it->second.as>(); + + for (size_t i = 0; i < inputs.size(); ++i) { + exec.arg(inputs[i].data(), arg_types[i]); + } + for (size_t i = 0, j = inputs.size(); i < outputs.size(); ++i, ++j) { + exec.arg(outputs[i].data(), arg_types[j]); + } + + exec(ctx); + + maybe_set_result_events(evaluationContext, ctx); + return true; +} + +bool MLIREvaluateGcGPU::invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) { + std::vector waitList; + gc::gpu::OclContext ctx = build_ocl_context(evaluationContext, waitList); + gc::gpu::DynamicExecutor exec(module); + + // Layout (5 pointers per memref, see MemRefDescriptor::append_to_packed_args + // in transformations/op/mlir_op.cpp): + // [aligned, rank, shape*, strides*, is_usm] + constexpr size_t kStride = 5; + OPENVINO_ASSERT(args.size() % kStride == 0, + "[GPU] MLIREvaluateGcGPU::invoke_packed: malformed args vector"); + for (size_t i = 0; i < args.size(); i += kStride) { + exec.arg( + /*alignedPtr=*/args[i], + /*rank=*/reinterpret_cast(args[i + 1]), + /*shape=*/reinterpret_cast(args[i + 2]), + /*strides=*/reinterpret_cast(args[i + 3]), + /*isUsm=*/reinterpret_cast(args[i + 4]) != 0 + ); + } + exec(ctx); + maybe_set_result_events(evaluationContext, ctx); + return true; +} + +void MLIREvaluateGcGPU::maybe_set_result_events(const ov::EvaluationContext& evaluationContext, + gc::gpu::OclContext& ctx) { + auto events_it = evaluationContext.find(ov::internal::mlir_meta::result_events.name()); + if (events_it == evaluationContext.end()) + return; + + auto retain_event = [](cl_event event) { + const auto err = clRetainEvent(event); + if (err != CL_SUCCESS) { + OPENVINO_THROW("Failed to retain MLIR result event, error: ", err); + } + }; + + auto* events = events_it->second.as*>(); + events->reserve(events->size() + ctx.events.size()); + for (auto event : ctx.events) { + retain_event(event); + events->push_back(event); + } +} + +gc::gpu::OclContext MLIREvaluateGcGPU::build_ocl_context(const ov::EvaluationContext& evaluationContext, + std::vector& waitList) { + auto it = evaluationContext.find(ov::intel_gpu::ocl_queue.name()); + if (it == evaluationContext.end()) { + OPENVINO_THROW("No queue provided for OpenCL execution"); + } + cl_command_queue queue = reinterpret_cast(it->second.as()); + + uint32_t waitListLen = 0; + + it = evaluationContext.find(ov::internal::mlir_meta::wait_list.name()); + if (it != evaluationContext.end()) { + waitList = it->second.as>(); + waitListLen = waitList.size(); + } + + const bool createEvents = evaluationContext.count(ov::internal::mlir_meta::result_events.name()) != 0; + return gc::gpu::OclContext(module->runtime, queue, createEvents, waitListLen, + reinterpret_cast(waitList.data())); +} + +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.hpp new file mode 100644 index 00000000000000..9222962bda7d70 --- /dev/null +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.hpp @@ -0,0 +1,45 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include + +#include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" +#include "mlir/ExecutionEngine/ExecutionEngine.h" +#include "mlir/IR/BuiltinOps.h" +#include "interface/mlir_evaluate_base.hpp" +#include "openvino/core/any.hpp" +#include "openvino/core/node.hpp" +#include "openvino/runtime/tensor.hpp" + +namespace ov::intel_gpu::mlir { + +using ::mlir::ExecutionEngine; +using ::mlir::ModuleOp; +using ::mlir::OwningOpRef; + +class MLIREvaluateGcGPU : public MLIREvaluateBase { + std::shared_ptr module; + +public: + MLIREvaluateGcGPU(OwningOpRef _module, + std::shared_ptr loweringContext); + + bool requires_packed_args() const override { return !module->isStatic; } + bool invoke(const ov::TensorVector& inputs, + ov::TensorVector& outputs, + const ov::EvaluationContext& evaluationContext) override; + bool invoke_packed(std::vector& args, + const ov::EvaluationContext& evaluationContext) override; + +private: + ::mlir::gc::gpu::OclContext build_ocl_context(const ov::EvaluationContext& evaluationContext, + std::vector& waitList); + static void maybe_set_result_events(const ov::EvaluationContext& evaluationContext, + ::mlir::gc::gpu::OclContext& ctx); +}; + +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp deleted file mode 100644 index f9a6fd4da7b749..00000000000000 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.cpp +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include "mlir_op.hpp" -#include "transformations/mlir/convert.hpp" - -#include -#include -#include -#include -#include -#include - -#include "mlir/Dialect/Bufferization/Transforms/Passes.h" -#include "mlir/Pass/PassManager.h" - -// TODO: Prune unused headers -- it's hard to understand needed ones -#include "llvm/MC/TargetRegistry.h" -#include "llvm/Support/SourceMgr.h" -#include "mlir/Dialect/MemRef/Transforms/Passes.h" -#include "mlir/Dialect/Bufferization/Transforms/Passes.h" -#include "mlir/Dialect/Linalg/TransformOps/DialectExtension.h" -#include "mlir/ExecutionEngine/JitRunner.h" -#include "mlir/Pass/PassManager.h" -#include "mlir/Support/LLVM.h" - -#include "gc/Transforms/Passes.h" -#include "gc/Utils/Error.h" -#include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" -#include "openvino/runtime/intel_gpu/remote_properties.hpp" -#include "properties.hpp" - -namespace { - -using namespace mlir; - -using NodePtr = std::shared_ptr; -using SymbolPtr = std::shared_ptr; - -void prepareMLIRKernelWithoutWrapper(mlir::OwningOpRef& module) { - PassManager pm(module->getContext()); - - gc::GPUPipelineOptions opts; - gc::populateGPUPipeline(pm, opts); - - auto result = pm.run(module.get()); - if (failed(result)) { - llvm::errs() << "ERROR: Failed to lower IR to LLVM dialect\n"; - module->print(llvm::errs()); - } -} - -// TODO: u4/i4 types are not supported -struct MemRefDescriptor { - MemRefDescriptor() = default; - - MemRefDescriptor (ov::Tensor tensor, const ov::PartialShape& module_input_shape) - : allocated(tensor.data()), - aligned(tensor.data()), - offset(0) { - if (module_input_shape.rank() == shape_size(tensor.get_shape())) { - shape.assign(tensor.get_shape().begin(), tensor.get_shape().end()); - } else { - auto it = tensor.get_shape().begin(); - std::advance(it, module_input_shape.rank().get_length()); - shape.assign(tensor.get_shape().begin(), it); - - if (std::any_of(it, tensor.get_shape().end(), [](size_t dim) {return dim != 1;})) { - OPENVINO_THROW("Mismatch in shape sizes"); - } - } - - strides.resize(shape.size()); - const auto& byte_strides = tensor.get_strides(); - auto element_size = tensor.get_element_type().size(); - for (size_t i = 0; i < strides.size(); ++i) { - assert(byte_strides[i] % element_size == 0); - // TODO: handle case when stride is not aligned (restrict at OV API level) - strides[i] = byte_strides[i] / element_size; - //std::cerr << "stride [" << i << "] = " << strides[i] << "\n"; - } - } - - MemRefDescriptor (ov::Tensor tensor) - : MemRefDescriptor(tensor, tensor.get_shape()) {} - - void* allocated; - void* aligned; - int64_t offset; - std::vector shape; - std::vector strides; - - // Pack into a fixed-stride layout consumed by MLIREvaluateGcGPU::invoke_packed: - // [aligned, rank, shape*, strides*, is_usm] - void append_to_packed_args(std::vector& args, bool is_usm) { - args.push_back(aligned); - args.push_back(reinterpret_cast(shape.size())); - args.push_back(shape.data()); - args.push_back(strides.data()); - args.push_back(reinterpret_cast(static_cast(is_usm))); - } -}; - -} // namespace - -namespace ov { -namespace mlir { - -using namespace ::mlir; - -cl_device_id extract_device_from_context(cl_context context) { - size_t devices_size; - cl_int err = clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &devices_size); - if (err != CL_SUCCESS) { - OPENVINO_THROW("Error getting context info: ", err); - } - if (devices_size / sizeof(cl_device_id) != 1) { - OPENVINO_THROW("Expected exactly one device in the context, got ", devices_size); - } - - cl_device_id devices; - err = clGetContextInfo(context, CL_CONTEXT_DEVICES, devices_size, &devices, NULL); - if (err != CL_SUCCESS) { - OPENVINO_THROW("Error getting device IDs: ", err); - } - - return devices; -} - -MLIREvaluateGcGPU::MLIREvaluateGcGPU(OwningOpRef _module, std::shared_ptr loweringContext) { - OPENVINO_MLIR_DEBUG_PRINT( - "[ DEBUG ] Source MLIR:\n" - "-----------------------------------------\n"); - OPENVINO_MLIR_DEBUG(_module->dump()); - OPENVINO_MLIR_DEBUG_PRINT( - "-----------------------------------------\n"); - - gc::gpu::OclModuleBuilderOpts opts; - OPENVINO_MLIR_DEBUG(opts.dumpIr = true); - gc::gpu::OclModuleBuilder builder(std::move(_module), opts); - - auto it = loweringContext->find(ov::intel_gpu::ocl_context.name()); - if (it == loweringContext->end()) { - OPENVINO_THROW("No cl_context provided for OpenCL execution"); - } - auto context = reinterpret_cast(it->second.as()); - // assuming there's always one device per context - auto device = extract_device_from_context(context); - - OPENVINO_MLIR_DEBUG_PRINT( - "[ DEBUG ] Target LLVM:\n" - "-----------------------------------------\n"); - if (auto mod = builder.build(device, context)) { - module = *mod; - } else { - OPENVINO_THROW("Failed to build gc::gpuOclModule module"); - } - OPENVINO_MLIR_DEBUG_PRINT( - "-----------------------------------------\n"); -}; - -bool MLIREvaluateGcGPU::invoke(const ov::TensorVector& inputs, ov::TensorVector& outputs, const ov::EvaluationContext& evaluationContext) { - std::vector waitList; - gc::gpu::OclContext ctx = build_ocl_context(evaluationContext, waitList); - gc::gpu::StaticExecutor exec(module); - - auto it = evaluationContext.find(ov::internal::mlir_meta::is_kernel_arg_usm.name()); - if (it == evaluationContext.end()) { - OPENVINO_THROW("No is_kernel_arg_usm provided for OpenCL execution"); - } - std::vector arg_types = it->second.as>(); - - for (size_t i = 0; i < inputs.size(); ++i) { - exec.arg(inputs[i].data(), arg_types[i]); - } - for (size_t i = 0, j = inputs.size(); i < outputs.size(); ++i, ++j) { - exec.arg(outputs[i].data(), arg_types[j]); - } - - exec(ctx); - - maybe_set_result_events(evaluationContext, ctx); - return true; -} - -bool MLIREvaluateGcGPU::invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) { - std::vector waitList; - gc::gpu::OclContext ctx = build_ocl_context(evaluationContext, waitList); - gc::gpu::DynamicExecutor exec(module); - - // Layout (5 pointers per memref, see MemRefDescriptor::append_to_packed_args): - // [aligned, rank, shape*, strides*, is_usm] - constexpr size_t kStride = 5; - OPENVINO_ASSERT(args.size() % kStride == 0, - "[GPU] MLIREvaluateGcGPU::invoke_packed: malformed args vector"); - for (size_t i = 0; i < args.size(); i += kStride) { - exec.arg( - /*alignedPtr=*/args[i], - /*rank=*/reinterpret_cast(args[i + 1]), - /*shape=*/reinterpret_cast(args[i + 2]), - /*strides=*/reinterpret_cast(args[i + 3]), - /*isUsm=*/reinterpret_cast(args[i + 4]) != 0 - ); - } - exec(ctx); - maybe_set_result_events(evaluationContext, ctx); - return true; -} - -void MLIREvaluateGcGPU::maybe_set_result_events(const ov::EvaluationContext& evaluationContext, gc::gpu::OclContext& ctx) { - auto events_it = evaluationContext.find(ov::internal::mlir_meta::result_events.name()); - if (events_it == evaluationContext.end()) - return; - - auto retain_event = [](cl_event event) { - const auto err = clRetainEvent(event); - if (err != CL_SUCCESS) { - OPENVINO_THROW("Failed to retain MLIR result event, error: ", err); - } - }; - - auto* events = events_it->second.as*>(); - events->reserve(events->size() + ctx.events.size()); - for (auto event : ctx.events) { - retain_event(event); - events->push_back(event); - } -} - -gc::gpu::OclContext MLIREvaluateGcGPU::build_ocl_context(const ov::EvaluationContext& evaluationContext, std::vector& waitList) { - auto it = evaluationContext.find(ov::intel_gpu::ocl_queue.name()); - if (it == evaluationContext.end()) { - OPENVINO_THROW("No queue provided for OpenCL execution"); - } - cl_command_queue queue = reinterpret_cast(it->second.as()); - - uint32_t waitListLen = 0; - - it = evaluationContext.find(ov::internal::mlir_meta::wait_list.name()); - if (it != evaluationContext.end()) { - waitList = it->second.as>(); - waitListLen = waitList.size(); - } - - const bool createEvents = evaluationContext.count(ov::internal::mlir_meta::result_events.name()) != 0; - return gc::gpu::OclContext(module->runtime, queue, createEvents, - waitListLen, reinterpret_cast(waitList.data())); -} - -MLIROp::MLIROp(const ov::OutputVector& args, std::shared_ptr engine, const OVOutputTypes& output_types, const DimensionsMap& dimensions_map) - : Op(args), - engine(engine), - output_types(output_types), - dimensions_map(dimensions_map) { - constructor_validate_and_infer_types(); -} - -std::vector MLIROp::shape_infer(const std::vector& input_shapes) const { - OPENVINO_ASSERT(dimensions_map.size() == output_types.size(), - "MLIROp::shape_infer: dimensions_map size (", dimensions_map.size(), - ") does not match output_types size (", output_types.size(), ")"); - - std::vector output_shapes; - output_shapes.reserve(output_types.size()); - for (size_t i = 0; i < output_types.size(); ++i) { - ov::PartialShape resolved = std::get<1>(output_types[i]); - OPENVINO_ASSERT(dimensions_map[i].size() == resolved.size(), - "MLIROp::shape_infer: dimensions_map[", i, "] size (", dimensions_map[i].size(), - ") does not match output ", i, " rank (", resolved.size(), ")"); - - for (size_t j = 0; j < resolved.size(); ++j) { - if (!resolved[j].is_dynamic()) { - continue; - } - size_t input_index, dim_index; - std::tie(input_index, dim_index) = dimensions_map[i][j]; - OPENVINO_ASSERT(input_index < input_shapes.size(), - "MLIROp::shape_infer: dimensions_map[", i, "][", j, "] refers to input ", - input_index, " but only ", input_shapes.size(), " input shapes provided"); - OPENVINO_ASSERT(dim_index < input_shapes[input_index].size(), - "MLIROp::shape_infer: dimensions_map[", i, "][", j, "] refers to dim ", - dim_index, " of input ", input_index, " (rank ", - input_shapes[input_index].size(), ")"); - resolved[j] = input_shapes[input_index][dim_index]; - } - output_shapes.push_back(resolved); - } - return output_shapes; -} - -void MLIROp::validate_and_infer_types() { - std::vector input_shapes; - input_shapes.reserve(get_input_size()); - for (size_t i = 0; i < get_input_size(); ++i) { - input_shapes.push_back(get_input_partial_shape(i)); - } - auto output_shapes = shape_infer(input_shapes); - - set_output_size(output_types.size()); - for (size_t i = 0; i < output_types.size(); ++i) { - set_output_type(i, std::get<0>(output_types[i]), output_shapes[i]); - } -} - -NodePtr MLIROp::clone_with_new_inputs(const ov::OutputVector& new_args) const { - return std::make_shared(new_args, engine, output_types, dimensions_map); -} - -bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs, const ov::EvaluationContext& evaluationContext) const { - if (!engine->requires_packed_args()) { - return engine->invoke(inputs, outputs, evaluationContext); - } - - std::vector memref_args; - memref_args.reserve(inputs.size() + outputs.size()); - for (size_t i = 0; i < inputs.size(); ++i) { - auto& initial_shape = get_input_partial_shape(i); - memref_args.emplace_back(inputs[i], initial_shape); - } - for (size_t i = 0; i < outputs.size(); ++i) { - // TODO: Optimize by adding all dimensions to dimensions_map, not only dynamic - Shape target; - PartialShape expected = get_output_partial_shape(i); - for(size_t j = 0; j < expected.size(); ++j) { - auto dim = expected[j]; - if(dim.is_dynamic()) { - int input_index, dim_index; - std::tie(input_index, dim_index) = dimensions_map[i][j]; - target.push_back(inputs[input_index].get_shape()[dim_index]); - } else { - target.push_back(dim.get_length()); - } - } - outputs[i].set_shape(target); - memref_args.emplace_back(outputs[i]); - } - - std::vector is_usm; - auto it = evaluationContext.find(ov::internal::mlir_meta::is_kernel_arg_usm.name()); - if (it != evaluationContext.end()) { - is_usm = it->second.as>(); - } else { - is_usm.assign(memref_args.size(), false); - } - OPENVINO_ASSERT(is_usm.size() == memref_args.size(), - "[GPU] MLIROp::evaluate: is_usm and memref count mismatch"); - - std::vector args; - for (size_t k = 0; k < memref_args.size(); ++k) { - memref_args[k].append_to_packed_args(args, is_usm[k]); - } - - return engine->invoke_packed(args, evaluationContext); -} - -bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const { - return evaluate(outputs, inputs, ov::EvaluationContext()); -} - -bool MLIROp::has_evaluate() const { - return true; -} - -std::vector mlir_op_shape_infer( - const std::shared_ptr& op, - const std::vector& input_shapes) { - auto mlir_op = std::dynamic_pointer_cast(op); - OPENVINO_ASSERT(mlir_op != nullptr, "mlir_op_shape_infer expects an MLIROp node"); - return mlir_op->shape_infer(input_shapes); -} - -} // namespace mlir -} // namespace ov \ No newline at end of file diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.hpp deleted file mode 100644 index e400b7db26d0d5..00000000000000 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_op.hpp +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (C) 2018-2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#pragma once - -#include "mlir/IR/BuiltinOps.h" -#include "mlir/ExecutionEngine/ExecutionEngine.h" - -#include "openvino/op/op.hpp" - -#include "common/convert_common.hpp" - -#include "gc/ExecutionEngine/GPURuntime/GpuOclRuntime.h" - -namespace ov { -namespace mlir { - -using ::mlir::OwningOpRef; -using ::mlir::ModuleOp; -using ::mlir::ExecutionEngine; - -class MLIROp; - -class MLIREvaluateBase { -public: - virtual bool requires_packed_args() const = 0; - // ::invoke() doesn't require any args preprocessing so we can pass tensors as is - virtual bool invoke(const ov::TensorVector& inputs, ov::TensorVector& outputs, const ov::EvaluationContext& evaluationContext) = 0; - virtual bool invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) = 0; - virtual ~MLIREvaluateBase() = default; -}; - -class MLIREvaluateGcGPU : public MLIREvaluateBase { - std::shared_ptr module; - -public: - MLIREvaluateGcGPU(OwningOpRef _module, std::shared_ptr loweringContext); - - bool requires_packed_args() const override { return !module->isStatic; } - bool invoke(const ov::TensorVector& inputs, ov::TensorVector& outputs, const ov::EvaluationContext& evaluationContext) override; - bool invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) override; - -private: - gc::gpu::OclContext build_ocl_context(const ov::EvaluationContext& evaluationContext, std::vector& waitList); - static void maybe_set_result_events(const ov::EvaluationContext& evaluationContext, gc::gpu::OclContext& ctx); -}; - -// Maps [output index][dimension index] -> [input index][dimension index] to infer shapes for entire subgraph -using DimensionsMap = std::vector>>; - - -class MLIROp : public ov::op::Op { - std::shared_ptr engine; - OVOutputTypes output_types; - DimensionsMap dimensions_map; - -public: - - OPENVINO_OP("MLIROp"); - - MLIROp(const ov::OutputVector& args, std::shared_ptr engine, - const OVOutputTypes& output_types, const DimensionsMap& dimensions_map); - void validate_and_infer_types() override; - NodePtr clone_with_new_inputs(const ov::OutputVector& new_args) const override; - bool evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const override; - bool evaluate(ov::TensorVector& output_values, const ov::TensorVector& input_values, - const ov::EvaluationContext& evaluationContext) const override; - bool has_evaluate() const override; - std::vector shape_infer(const std::vector& input_shapes) const; -}; - -} // namespace mlir -} // namespace ov \ No newline at end of file diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp index d3311dd65ce0a6..02df629883bbf2 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp @@ -9,8 +9,7 @@ #include "subgraph_tracker.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { void Subgraph::merge (Subgraph& other) { @@ -191,5 +190,4 @@ void SubgraphTracker::try_terminate_subgraphs(const Dependencies& subgraphs, Nod } -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp index 07411d1b575a74..b09bbf869fd199 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp @@ -9,8 +9,7 @@ #include "common/typedefs.hpp" -namespace ov { -namespace mlir { +namespace ov::intel_gpu::mlir { struct Subgraph { ov::NodeVector nodes; @@ -64,5 +63,4 @@ class SubgraphTracker { }; -} // namespace mlir -} // namespace ov +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp new file mode 100644 index 00000000000000..514a8f28dea379 --- /dev/null +++ b/src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp @@ -0,0 +1,194 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#ifdef GRAPH_COMPILER + +#include "intel_gpu/op/mlir_op.hpp" + +#include +#include +#include + +#include "openvino/core/shape.hpp" +#include "../mlir/interface/mlir_evaluate_base.hpp" +#include "../mlir/interface/properties.hpp" + +namespace ov::intel_gpu::op { + +namespace { + +// Descriptor packed into the 5-slot layout expected by +// MLIREvaluateGcGPU::invoke_packed: +// [aligned, rank, shape*, strides*, is_usm] +// TODO: u4/i4 types are not supported +struct MemRefDescriptor { + MemRefDescriptor() = default; + + MemRefDescriptor(ov::Tensor tensor, const ov::PartialShape& module_input_shape) + : allocated(tensor.data()), + aligned(tensor.data()), + offset(0) { + if (module_input_shape.rank() == shape_size(tensor.get_shape())) { + shape.assign(tensor.get_shape().begin(), tensor.get_shape().end()); + } else { + auto it = tensor.get_shape().begin(); + std::advance(it, module_input_shape.rank().get_length()); + shape.assign(tensor.get_shape().begin(), it); + + if (std::any_of(it, tensor.get_shape().end(), [](size_t dim) { return dim != 1; })) { + OPENVINO_THROW("Mismatch in shape sizes"); + } + } + + strides.resize(shape.size()); + const auto& byte_strides = tensor.get_strides(); + auto element_size = tensor.get_element_type().size(); + for (size_t i = 0; i < strides.size(); ++i) { + assert(byte_strides[i] % element_size == 0); + // TODO: handle case when stride is not aligned (restrict at OV API level) + strides[i] = byte_strides[i] / element_size; + } + } + + explicit MemRefDescriptor(ov::Tensor tensor) : MemRefDescriptor(tensor, tensor.get_shape()) {} + + void* allocated; + void* aligned; + int64_t offset; + std::vector shape; + std::vector strides; + + void append_to_packed_args(std::vector& args, bool is_usm) { + args.push_back(aligned); + args.push_back(reinterpret_cast(shape.size())); + args.push_back(shape.data()); + args.push_back(strides.data()); + args.push_back(reinterpret_cast(static_cast(is_usm))); + } +}; + +} // namespace + +MLIROp::MLIROp(const ov::OutputVector& args, + std::shared_ptr engine, + const OVOutputTypes& output_types, + const DimensionsMap& dimensions_map) + : Op(args), + engine(std::move(engine)), + output_types(output_types), + dimensions_map(dimensions_map) { + constructor_validate_and_infer_types(); +} + +std::vector MLIROp::shape_infer(const std::vector& input_shapes) const { + OPENVINO_ASSERT(dimensions_map.size() == output_types.size(), + "MLIROp::shape_infer: dimensions_map size (", dimensions_map.size(), + ") does not match output_types size (", output_types.size(), ")"); + + std::vector output_shapes; + output_shapes.reserve(output_types.size()); + for (size_t i = 0; i < output_types.size(); ++i) { + ov::PartialShape resolved = std::get<1>(output_types[i]); + OPENVINO_ASSERT(dimensions_map[i].size() == resolved.size(), + "MLIROp::shape_infer: dimensions_map[", i, "] size (", dimensions_map[i].size(), + ") does not match output ", i, " rank (", resolved.size(), ")"); + + for (size_t j = 0; j < resolved.size(); ++j) { + if (!resolved[j].is_dynamic()) { + continue; + } + size_t input_index, dim_index; + std::tie(input_index, dim_index) = dimensions_map[i][j]; + OPENVINO_ASSERT(input_index < input_shapes.size(), + "MLIROp::shape_infer: dimensions_map[", i, "][", j, "] refers to input ", + input_index, " but only ", input_shapes.size(), " input shapes provided"); + OPENVINO_ASSERT(dim_index < input_shapes[input_index].size(), + "MLIROp::shape_infer: dimensions_map[", i, "][", j, "] refers to dim ", + dim_index, " of input ", input_index, " (rank ", + input_shapes[input_index].size(), ")"); + resolved[j] = input_shapes[input_index][dim_index]; + } + output_shapes.push_back(resolved); + } + return output_shapes; +} + +void MLIROp::validate_and_infer_types() { + std::vector input_shapes; + input_shapes.reserve(get_input_size()); + for (size_t i = 0; i < get_input_size(); ++i) { + input_shapes.push_back(get_input_partial_shape(i)); + } + auto output_shapes = shape_infer(input_shapes); + + set_output_size(output_types.size()); + for (size_t i = 0; i < output_types.size(); ++i) { + set_output_type(i, std::get<0>(output_types[i]), output_shapes[i]); + } +} + +std::shared_ptr MLIROp::clone_with_new_inputs(const ov::OutputVector& new_args) const { + return std::make_shared(new_args, engine, output_types, dimensions_map); +} + +bool MLIROp::evaluate(ov::TensorVector& outputs, + const ov::TensorVector& inputs, + const ov::EvaluationContext& evaluationContext) const { + if (!engine->requires_packed_args()) { + return engine->invoke(inputs, outputs, evaluationContext); + } + + std::vector memref_args; + memref_args.reserve(inputs.size() + outputs.size()); + for (size_t i = 0; i < inputs.size(); ++i) { + const auto& initial_shape = get_input_partial_shape(i); + memref_args.emplace_back(inputs[i], initial_shape); + } + for (size_t i = 0; i < outputs.size(); ++i) { + // TODO: Optimize by adding all dimensions to dimensions_map, not only dynamic + ov::Shape target; + ov::PartialShape expected = get_output_partial_shape(i); + for (size_t j = 0; j < expected.size(); ++j) { + auto dim = expected[j]; + if (dim.is_dynamic()) { + size_t input_index, dim_index; + std::tie(input_index, dim_index) = dimensions_map[i][j]; + target.push_back(inputs[input_index].get_shape()[dim_index]); + } else { + target.push_back(dim.get_length()); + } + } + outputs[i].set_shape(target); + memref_args.emplace_back(outputs[i]); + } + + std::vector is_usm; + auto it = evaluationContext.find(ov::internal::mlir_meta::is_kernel_arg_usm.name()); + if (it != evaluationContext.end()) { + is_usm = it->second.as>(); + } else { + is_usm.assign(memref_args.size(), false); + } + OPENVINO_ASSERT(is_usm.size() == memref_args.size(), + "[GPU] MLIROp::evaluate: is_usm and memref count mismatch"); + + std::vector args; + for (size_t k = 0; k < memref_args.size(); ++k) { + memref_args[k].append_to_packed_args(args, is_usm[k]); + } + + return engine->invoke_packed(args, evaluationContext); +} + +bool MLIROp::evaluate(ov::TensorVector& outputs, const ov::TensorVector& inputs) const { + return evaluate(outputs, inputs, ov::EvaluationContext()); +} + +bool MLIROp::has_evaluate() const { + return true; +} + +} // namespace ov::intel_gpu::op + +#endif // GRAPH_COMPILER diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index 096b0260c91671..895a7ed3b1f34d 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -153,7 +153,9 @@ #include "transformations/init_node_info.hpp" #include "transformations/normalize_l2_decomposition.hpp" #include "transformations/low_precision/mark_dequantization_subgraph.hpp" -#include "transformations/mlir/convert.hpp" +#ifdef GRAPH_COMPILER +#include "transformations/mlir/interface/convert.hpp" +#endif // GRAPH_COMPILER #include "transformations/op_conversions/bidirectional_sequences_decomposition.hpp" #include "transformations/op_conversions/convert_batch_to_space.hpp" #include "transformations/op_conversions/convert_broadcast3.hpp" @@ -702,7 +704,7 @@ void TransformationsPipeline::apply(std::shared_ptr func) { convert_input_output_precision, store_original_precision_as_rt_attribute); - if (ov::pass::is_mlir_transform_enabled()) { + if (config.get_enable_mlir()) { pass_config->disable(); pass_config->disable(); pass_config->disable(); @@ -1723,16 +1725,23 @@ void TransformationsPipeline::apply(std::shared_ptr func) { manager.register_pass(); + if (config.get_enable_mlir()) { #ifdef GRAPH_COMPILER - auto loweringContext = std::make_shared(); - auto it = m_context->get_property().find(ov::intel_gpu::ocl_context.name()); - if (it != m_context->get_property().end()) { - // We assume here that there's only one device per context and that an - // actual device will be extracted later by the 'mlir_op'. - loweringContext->insert(ov::intel_gpu::ocl_context(it->second.as())); - } - ov::pass::transformMLIR(func, loweringContext); + auto loweringContext = std::make_shared(); + auto it = m_context->get_property().find(ov::intel_gpu::ocl_context.name()); + if (it != m_context->get_property().end()) { + // We assume here that there's only one device per context and that an + // actual device will be extracted later by the 'mlir_op'. + loweringContext->insert(ov::intel_gpu::ocl_context(it->second.as())); + } + ov::intel_gpu::mlir::transformMLIR(func, loweringContext); +#else + OPENVINO_THROW( + "[GPU] Property 'GPU_ENABLE_MLIR' (or OV_GPU_ENABLE_MLIR env var) is enabled, " + "but the plugin was built without Graph Compiler support. " + "Rebuild OpenVINO with -DENABLE_GRAPH_COMPILER=ON to enable MLIR execution."); #endif + } // This is supposed to be the last pass to ensure that we don't have name collisions until // GPU plugin stops using friendly names for program creation diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp index c1ed538f7e327d..0cf1e4fe38a703 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp @@ -17,10 +17,17 @@ #include "openvino/op/transpose.hpp" #include "shared_test_classes/base/benchmark.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" -#include "transformations/mlir/convert.hpp" +#include "common_test_utils/ov_plugin_cache.hpp" +#include "openvino/runtime/intel_gpu/properties.hpp" namespace { +static bool is_mlir_enabled() { + return ov::test::utils::PluginCache::get() + .core()->get_property(ov::test::utils::DEVICE_GPU, + ov::intel_gpu::enable_mlir); +} + // A(1xSEQx1536xf16) // ▼ // MatMul(transpose B) → Add(const) → Reshape(1xSEQx24x64) → Transpose(1x24xSEQx64) @@ -116,7 +123,7 @@ TEST_P(MatMulRmsnormTest, Inference) { run(); } TEST_P(MatMulRmsnormBenchmark, Inference) { - if (ov::pass::is_mlir_transform_enabled()) + if (is_mlir_enabled()) run_benchmark("MLIROp"); else run_benchmark({"FullyConnected", "Add", "Reshape", "Transpose", "RMS"}); @@ -175,7 +182,7 @@ TEST_P(MatMulRmsnormConcatTest, Inference) { run(); } TEST_P(MatMulRmsnormConcatBenchmark, Inference) { - if (ov::pass::is_mlir_transform_enabled()) + if (is_mlir_enabled()) run_benchmark("MLIROp"); else run_benchmark({"FullyConnected", "Add", "Reshape", "Transpose", "RMS", "Concat"}); diff --git a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp index 9ea76b518c4ca1..b682a91c35a63e 100644 --- a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp +++ b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp @@ -11,11 +11,18 @@ #include "openvino/op/add.hpp" #include "openvino/op/multiply.hpp" #include "openvino/runtime/intel_gpu/properties.hpp" -#include "transformations/mlir/convert.hpp" +#include "common_test_utils/ov_plugin_cache.hpp" +#include "openvino/runtime/intel_gpu/properties.hpp" namespace { using ov::test::InputShape; + +static bool is_mlir_enabled() { + return ov::test::utils::PluginCache::get() + .core()->get_property(ov::test::utils::DEVICE_GPU, + ov::intel_gpu::enable_mlir); +} using ReduceInputParams = std::tuple< ov::Shape, // Input shapes ov::element::Type, // Input precision @@ -68,7 +75,7 @@ class ReduceSumSqueezeTest : public testing::WithParamInterface(add_node, mul_val_node); auto result = std::make_shared(mul_node); function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input_node}, "input"); - if (ov::pass::is_mlir_transform_enabled()) + if (is_mlir_enabled()) abs_threshold = 0.01f; } @@ -83,7 +90,7 @@ class ReduceSumSqueezeTest : public testing::WithParamInterface Date: Fri, 24 Jul 2026 13:01:35 +0200 Subject: [PATCH 087/121] General clean-up (#12) Signed-off-by: dchigarev --- CMakeLists.txt | 1 - GC_BUILD.MD | 54 -- scripts/build_mlir.sh | 16 - setup.py | 16 +- src/bindings/python/wheel/CMakeLists.txt | 6 +- src/cmake/openvino.cmake | 1 - src/common/transformations/CMakeLists.txt | 10 +- ...ed_dot_product_attention_decomposition.cpp | 5 - src/core/CMakeLists.txt | 8 +- src/plugins/intel_gpu/CMakeLists.txt | 3 +- .../intel_gpu/src/plugin/program_builder.cpp | 9 +- .../src/plugin/transformations_pipeline.cpp | 6 +- .../intel_gpu/tests/functional/CMakeLists.txt | 5 - .../mlir_op/models/matmul_64_128_f16.bin | Bin 32768 -> 0 bytes .../mlir_op/models/matmul_64_128_f16.xml | 83 --- .../mlir_op/models/matmul_64_128_f32.bin | Bin 65536 -> 0 bytes .../mlir_op/models/matmul_64_128_f32.xml | 83 --- .../functional/mlir_op/models/sdpa_test.xml | 134 ----- .../tests/functional/mlir_op/sanity_tests.cpp | 479 ------------------ .../test_utils}/opencl_helper_instance.hpp | 20 +- .../tools/compile_tool/CMakeLists.txt | 1 - .../shared_test_classes/base/benchmark.hpp | 14 +- .../functional/plugin/shared/CMakeLists.txt | 8 - tools/mlir_bench/README.md | 72 --- tools/mlir_bench/libxsmm_bench.sh | 83 --- tools/mlir_bench/lora-runner.xsh | 136 ----- tools/mlir_bench/mlp_bench.sh | 123 ----- tools/mlir_bench/ov_model_gen.py | 255 ---------- tools/mlir_bench/ov_raw_mlir_bench.sh | 115 ----- tools/mlir_bench/run_bench_bf16.sh | 55 -- tools/mlir_bench/run_bench_f32.sh | 55 -- tools/mlir_bench/tpp_mlir_bench.sh | 93 ---- 32 files changed, 30 insertions(+), 1919 deletions(-) delete mode 100644 GC_BUILD.MD delete mode 100755 scripts/build_mlir.sh delete mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.bin delete mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml delete mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.bin delete mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.xml delete mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/models/sdpa_test.xml delete mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp rename src/plugins/intel_gpu/tests/{common => unit/test_utils}/opencl_helper_instance.hpp (81%) delete mode 100644 tools/mlir_bench/README.md delete mode 100755 tools/mlir_bench/libxsmm_bench.sh delete mode 100755 tools/mlir_bench/lora-runner.xsh delete mode 100755 tools/mlir_bench/mlp_bench.sh delete mode 100644 tools/mlir_bench/ov_model_gen.py delete mode 100755 tools/mlir_bench/ov_raw_mlir_bench.sh delete mode 100755 tools/mlir_bench/run_bench_bf16.sh delete mode 100755 tools/mlir_bench/run_bench_f32.sh delete mode 100755 tools/mlir_bench/tpp_mlir_bench.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 726625a4542ab6..8d234c44aef029 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,7 +141,6 @@ function(ov_developer_package_export_targets) "A list of OpenVINO Developer Package exported targets" FORCE) endfunction() - # # Build # diff --git a/GC_BUILD.MD b/GC_BUILD.MD deleted file mode 100644 index 694a37043aee60..00000000000000 --- a/GC_BUILD.MD +++ /dev/null @@ -1,54 +0,0 @@ -### Tested on x1-spr 'Triton (GPU, Agama 2521.10, DLE 2025.1.1, Ubuntu 22.04)' profile - -If you feel that these steps look outdated, check the actual CI-steps here: https://github.com/intel-sandbox/graph-compiler/blob/main/.github/workflows/ci.yml - -#### step 1: install opencl-headers & install llvm-env-vars -``` -sudo apt install -y intel-opencl-icd opencl-c-headers ocl-icd-opencl-dev -``` - -#### step 2: build graph-compiler + llvm - -``` -# clone -git clone https://github.com/intel-sandbox/graph-compiler.git -cd graph-compiler -# install nanobind -pip install nanobind - -./scripts/compile.sh -l -``` - -#### step 4: build openvino - -``` -git clone https://github.com/intel-sandbox/openvino-gc -cd openvino -git checkout mlir-gc-integration - -mkdir build && cd build -cmake -G Ninja -S "$ov_dir" -B "$ov_dir/build" \ - -DLLVM_DIR=$llvm_dir/lib/cmake/llvm \ - -DMLIR_DIR=$llvm_dir/lib/cmake/mlir \ - -DENABLE_GRAPH_COMPILER=ON \ - -DLLVM_DYLINK=ON \ - -DENABLE_INTEL_GPU=ON \ - -DENABLE_TESTS=ON \ - -DENABLE_ONEDNN_FOR_GPU=OFF \ - -DENABLE_INTEL_CPU=OFF \ - -DENABLE_INTEL_NPU=OFF \ - -DCMAKE_CXX_FLAGS="-DOV_GPU_OPENCL_HPP_HAS_UUID -DOV_GPU_OPENCL_HPP_HAS_BUS_INFO" \ - -DGraphCompiler_DIR=$gc_dir/lib/cmake/GraphCompiler \ - -DOpenCL_HPP_INCLUDE_DIR="$ov_dir/thirdparty/ocl/clhpp_headers/include" \ - -DOpenCL_HPP="$ov_dir/thirdparty/ocl/clhpp_headers/include/CL/opencl.hpp" -cmake --build "$ov_dir/build" -j16 -``` - -#### step 5: run sanity-mlir tests and dump mlir - -``` -OV_MLIR_DEBUG=1 OV_MLIR_MODE=GC_GPU ./bin/intel64/Release/ov_gpu_func_tests --gtest_filter=MLIRExecution.SimpleMatmulf16 -``` - -### CI runner -The CI runner is the X1 server - gcrun. When the session starts, it installs and starts itself automatically. The action scripts are located in the graph-compiler repository. diff --git a/scripts/build_mlir.sh b/scripts/build_mlir.sh deleted file mode 100755 index 4315efd0ca3758..00000000000000 --- a/scripts/build_mlir.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -# Run it in llvm-project/build directory - -cmake -G Ninja ../llvm \ - -DLLVM_ENABLE_PROJECTS="mlir" \ - -DLLVM_BUILD_EXAMPLES=ON \ - -DLLVM_INSTALL_UTILS=ON \ - -DLLVM_TARGETS_TO_BUILD="host" \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DLLVM_ENABLE_ASSERTIONS=ON \ - -DCMAKE_C_COMPILER=clang \ - -DCMAKE_CXX_COMPILER=clang++ \ - -DLLVM_USE_LINKER=lld - -ninja \ No newline at end of file diff --git a/setup.py b/setup.py index 01f5051a73d9d0..e85900573479b1 100644 --- a/setup.py +++ b/setup.py @@ -130,13 +130,6 @@ "install_dir": OV_RUNTIME_LIBS_DIR, "binary_dir": OPENVINO_BINARY_DIR, }, - "gc_libs": { - "name": "GcCpuRuntime", - "prefix": f"{BUILD_BASE}/libs.gc", - "install_dir": OV_RUNTIME_LIBS_DIR, - "rpath": LIBS_RPATH, - "binary_dir": OPENVINO_BINARY_DIR, - }, "pugixml_libs": { "name": "pugixml", "prefix": f"{BUILD_BASE}/libs.pugixml", @@ -191,6 +184,13 @@ "install_dir": OV_RUNTIME_LIBS_DIR, "rpath": LIBS_RPATH, "binary_dir": OPENVINO_BINARY_DIR, + }, + "gguf_libs": { + "name": "gguf", + "prefix": f"{BUILD_BASE}/libs.gguf", + "install_dir": OV_RUNTIME_LIBS_DIR, + "rpath": LIBS_RPATH, + "binary_dir": OPENVINO_BINARY_DIR, } } @@ -444,8 +444,6 @@ def resolve_symlinks(self, local_base_dir: Path): for real_name, symlink in file_dict.items(): os.unlink(symlink) os.rename(real_name, symlink) - if "libs.gc" not in str(local_base_dir): - os.rename(real_name, symlink) self.announce(f"Resolved symlink {symlink} as {real_name}", level=log.INFO) def copy_package_libs(self, src_dirs): diff --git a/src/bindings/python/wheel/CMakeLists.txt b/src/bindings/python/wheel/CMakeLists.txt index d5f03d2319d5b6..733f9e480e4a68 100644 --- a/src/bindings/python/wheel/CMakeLists.txt +++ b/src/bindings/python/wheel/CMakeLists.txt @@ -9,7 +9,7 @@ execute_process(COMMAND ${Python3_EXECUTABLE} -c "from packaging import tags; print(f'{tags.interpreter_name()}{tags.interpreter_version()}')" OUTPUT_VARIABLE PYTHON_TAG OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT PYTHON_TAG) - message(FATAL_ERROR "Failed to detect Python Tag via wheel.vendored.packaging.tags. Please, check 'wheel' dependency version update") + message(FATAL_ERROR "Failed to detect Python Tag via packaging.tags. Please, check 'packaging' dependency version update") endif() execute_process(COMMAND ${Python3_EXECUTABLE} -c "from setuptools.command.bdist_wheel import get_abi_tag; print(f'{get_abi_tag()}')" @@ -18,10 +18,10 @@ if(NOT ABI_TAG) message(FATAL_ERROR "Failed to detect ABI Tag via setuptools.command.bdist_wheel. Please, check 'setuptools' dependency version update") endif() -execute_process(COMMAND ${Python3_EXECUTABLE} -c "import wheel.vendored.packaging.tags as tags ; print(f'{next(tags.platform_tags())}')" +execute_process(COMMAND ${Python3_EXECUTABLE} -c "from packaging import tags; print(f'{next(tags.platform_tags())}')" OUTPUT_VARIABLE PLATFORM_TAG OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT PLATFORM_TAG) - message(FATAL_ERROR "Failed to detect Platform Tag via wheel.vendored.packaging.tags. Please, check 'wheel' dependency version update") + message(FATAL_ERROR "Failed to detect Platform Tag via packaging.tags. Please, check 'packaging' dependency version update") endif() # defines wheel architecture part of `PLATFORM_TAG` diff --git a/src/cmake/openvino.cmake b/src/cmake/openvino.cmake index 6676600872b367..8bea7347e8e0e9 100644 --- a/src/cmake/openvino.cmake +++ b/src/cmake/openvino.cmake @@ -58,7 +58,6 @@ target_link_libraries(${TARGET_NAME} PUBLIC $<$,$,9.1>>:stdc++fs> $<$,$,9.0>>:c++fs>) - if(BUILD_SHARED_LIBS) target_link_libraries(${TARGET_NAME} PRIVATE openvino::shutdown) endif() diff --git a/src/common/transformations/CMakeLists.txt b/src/common/transformations/CMakeLists.txt index ed263c94d9ddbb..273012c534df08 100644 --- a/src/common/transformations/CMakeLists.txt +++ b/src/common/transformations/CMakeLists.txt @@ -9,7 +9,6 @@ set(PUBLIC_HEADERS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") file(GLOB_RECURSE LIBRARY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) file(GLOB_RECURSE PUBLIC_HEADERS ${PUBLIC_HEADERS_DIR}/*.hpp) - # Create named folders for the sources within the .vcproj # Empty name lists them directly under the .vcproj @@ -31,18 +30,11 @@ ov_build_target_faster(${TARGET_NAME}_obj PCH_HEADER "src/precomp.hpp" ) -target_compile_features(${TARGET_NAME}_obj PUBLIC cxx_std_17) - -target_link_libraries(${TARGET_NAME}_obj PRIVATE - openvino::reference - openvino::itt - openvino::core::dev - openvino::shape_inference) +target_link_libraries(${TARGET_NAME}_obj PRIVATE openvino::reference openvino::itt openvino::core::dev openvino::shape_inference) target_include_directories(${TARGET_NAME}_obj PRIVATE "${PUBLIC_HEADERS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/src") - ov_add_clang_format_target(${TARGET_NAME}_clang FOR_TARGETS ${TARGET_NAME}_obj) ov_mark_target_as_cc(${TARGET_NAME}_obj) diff --git a/src/common/transformations/src/transformations/op_conversions/scaled_dot_product_attention_decomposition.cpp b/src/common/transformations/src/transformations/op_conversions/scaled_dot_product_attention_decomposition.cpp index 47c948fb904917..eaa4774e7370e5 100644 --- a/src/common/transformations/src/transformations/op_conversions/scaled_dot_product_attention_decomposition.cpp +++ b/src/common/transformations/src/transformations/op_conversions/scaled_dot_product_attention_decomposition.cpp @@ -49,11 +49,6 @@ ov::pass::ScaledDotProductAttentionDecomposition::ScaledDotProductAttentionDecom auto pattern_node = ov::pass::pattern::wrap_type(); matcher_pass_callback callback = [OV_CAPTURE_CPY_AND_THIS](Matcher& m) { - // FIXME: unconditionally disabling the decomposition for now - // so we can always lower SDPA to linalgx.attention. We have to disable - // it harsh since the 'enable_sdpa_optimization=false' parameter that should - // disable this transformation doesn't work for in 'common_optimizations' pass. - return false; auto& pattern_to_output = m.get_pattern_value_map(); auto node = ov::as_type_ptr(pattern_to_output.at(pattern_node).get_node_shared_ptr()); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3f3fd933ac7f5a..23e40625317e9f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -48,13 +48,7 @@ target_include_directories(openvino_core_dev INTERFACE $ $ $ - $ - # HACK: to make gpu properties from 'src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp' - # available in 'GPU plugin MLIR sources'. Need to figure out something better. - $ - # HACK: to make mlir properties from 'src/inference/dev_api/openvino/runtime/internal_properties.hpp' - # available in 'GPU plugin MLIR sources'. Need to figure out something better. - $) + $) target_include_directories(openvino_core_dev SYSTEM INTERFACE $:$>>) diff --git a/src/plugins/intel_gpu/CMakeLists.txt b/src/plugins/intel_gpu/CMakeLists.txt index d0b93720c0ea0e..46c96381cc5f8e 100644 --- a/src/plugins/intel_gpu/CMakeLists.txt +++ b/src/plugins/intel_gpu/CMakeLists.txt @@ -169,8 +169,7 @@ ov_add_plugin(NAME ${TARGET_NAME} target_compile_options(${TARGET_NAME} PRIVATE $<$:$,/Os,-Os>>) -target_link_libraries( - ${TARGET_NAME} PRIVATE openvino_intel_gpu_graph openvino::pugixml) +target_link_libraries(${TARGET_NAME} PRIVATE openvino_intel_gpu_graph openvino::pugixml) target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/) diff --git a/src/plugins/intel_gpu/src/plugin/program_builder.cpp b/src/plugins/intel_gpu/src/plugin/program_builder.cpp index 4c6fd7e235a90d..6ca09df83bc76d 100644 --- a/src/plugins/intel_gpu/src/plugin/program_builder.cpp +++ b/src/plugins/intel_gpu/src/plugin/program_builder.cpp @@ -4,7 +4,6 @@ #include "intel_gpu/runtime/internal_properties.hpp" #include "openvino/core/rt_info/weightless_caching_attributes.hpp" -#include "openvino/core/except.hpp" #include "openvino/op/constant.hpp" #include "openvino/op/split.hpp" #include "openvino/op/variadic_split.hpp" @@ -16,7 +15,6 @@ #include "intel_gpu/plugin/common_utils.hpp" #include "intel_gpu/plugin/program_builder.hpp" #include "intel_gpu/primitives/data.hpp" -#include #include "intel_gpu/runtime/itt.hpp" #include "intel_gpu/runtime/debug_configuration.hpp" #include "intel_gpu/primitives/mutable_data.hpp" @@ -227,11 +225,8 @@ void ProgramBuilder::CreateSingleLayerPrimitive(const std::shared_ptr& } if (!is_created) { - std::stringstream ss; - ov::write_all_to_stream(ss, "Operation: ", op->get_friendly_name(), - " of type ", op->get_type_name(), - "(", op->get_type_info().version_id, ") is not supported."); - OPENVINO_THROW(ss.str()); + OPENVINO_THROW("Operation: ", op->get_friendly_name(), + "(", op->get_type_info().version_id, ") is not supported"); } } diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index 895a7ed3b1f34d..ab46f063fe0e85 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -845,9 +845,13 @@ void TransformationsPipeline::apply(std::shared_ptr func) { } pass_config->set_callback([&](const std::shared_ptr node){ - if (!config.get_enable_sdpa_optimization()) + // Never decompose if mlir-path is enabled + if (config.get_enable_mlir()) return true; + if (!config.get_enable_sdpa_optimization()) + return false; + auto sdpa = ov::as_type_ptr(node); // TODO: sdpa_opt is not supporting sink_input for 1st token case yet constexpr size_t sink_idx = cldnn::scaled_dot_product_attention::ScaledDotProductAttentionInputIdx::SINK; diff --git a/src/plugins/intel_gpu/tests/functional/CMakeLists.txt b/src/plugins/intel_gpu/tests/functional/CMakeLists.txt index 379d7bc7f04e58..146ae028e053cc 100644 --- a/src/plugins/intel_gpu/tests/functional/CMakeLists.txt +++ b/src/plugins/intel_gpu/tests/functional/CMakeLists.txt @@ -11,7 +11,6 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") ov_add_compiler_flags(/wd4305) endif() -list(APPEND DEFINES TEST_MODELS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/mlir_op/models") list(APPEND DEFINES TEST_CUSTOM_OP_CONFIG_PATH="${CMAKE_CURRENT_SOURCE_DIR}/custom_op/custom_op.xml") ov_add_test_target( @@ -24,7 +23,6 @@ ov_add_test_target( INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} $/include/ - $ ${TEST_COMMON_INCLUDE_DIR} DEFINES ${DEFINES} @@ -39,9 +37,6 @@ ov_add_test_target( OV GPU ) -if(ENABLE_GRAPH_COMPILER) - target_compile_definitions(${TARGET_NAME} PRIVATE GRAPH_COMPILER) -endif() ov_gpu_set_runtime_interface_for(${TARGET_NAME}) diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.bin b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.bin deleted file mode 100644 index 5e8f00c71c2001838d6e0910638f518bc1022590..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 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 M+68x3r=ZaP0S`-);s5{u diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml deleted file mode 100644 index 7164dcf7a2e695..00000000000000 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f16.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - 64 - 128 - - - - - - - - 128 - 128 - - - - - - - - 64 - 128 - - - 128 - 128 - - - - - 64 - 128 - - - - - - - - 64 - 128 - - - 64 - 128 - - - - - 64 - 128 - - - - - - - 64 - 128 - - - - - - - - - - - - - - - - - - - diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.bin b/src/plugins/intel_gpu/tests/functional/mlir_op/models/matmul_64_128_f32.bin deleted file mode 100644 index 0d0176d36fc40c5259635cd63d28e9f87c4325ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65536 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 - - - - - - - 64 - 128 - - - - - - - - 128 - 128 - - - - - - - - 64 - 128 - - - 128 - 128 - - - - - 64 - 128 - - - - - - - - 64 - 128 - - - 64 - 128 - - - - - 64 - 128 - - - - - - - 64 - 128 - - - - - - - - - - - - - - - - - - - diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/models/sdpa_test.xml b/src/plugins/intel_gpu/tests/functional/mlir_op/models/sdpa_test.xml deleted file mode 100644 index eaceb435bf3bd6..00000000000000 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/models/sdpa_test.xml +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - - - - - 1 - 64 - 80 - - - - - - - - - - 1 - 128 - 80 - - - - - - - - - - 1 - 128 - 80 - - - - - - - - - - - 1 - 1 - 128 - - - - - - - - - - - - - - - - - - - - - - - - - 1 - 64 - 80 - - - 1 - 128 - 80 - - - 1 - 128 - 80 - - - 1 - 1 - 128 - - - - - - - - - 1 - 64 - 80 - - - - - - - - - 1 - 64 - 80 - - - - - - - - - - - - - - - - - - - - - diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp deleted file mode 100644 index 2207967d86a9f9..00000000000000 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sanity_tests.cpp +++ /dev/null @@ -1,479 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// -#include "openvino/frontend/extension.hpp" - -#include - -#include "common_test_utils/file_utils.hpp" -#include "common_test_utils/test_assertions.hpp" -#include "openvino/runtime/core.hpp" -#include "openvino/util/file_util.hpp" -#include - -#include -#include -#include "opencl_helper_instance.hpp" -#include "openvino/core/preprocess/pre_post_process.hpp" -#include "openvino/core/partial_shape.hpp" -#include "openvino/op/scaled_dot_product_attention.hpp" - -#include - -using testing::ElementsAreArray; - -static std::string model_full_path(const std::string& path) { - std::string base = TEST_MODELS_DIR; - return ov::util::make_path(base + "/" + path); -} - -template -static void multiply_matrices(const std::vector& matrix_a, const std::vector& matrix_b, - std::vector& result, size_t rows_a, size_t cols_a, size_t cols_b) { - // Initialize the result matrix with zero values (f32 accumulator) - std::vector tmp(result.size(), 0.0f); - - // Matrix multiplication logic using linear indexing - for (size_t i = 0; i < rows_a; ++i) { - for (size_t j = 0; j < cols_b; ++j) { - for (size_t k = 0; k < cols_a; ++k) { - tmp[i * cols_b + j] += matrix_a[i * cols_a + k] * matrix_b[k * cols_b + j]; - } - } - } - - for (size_t i = 0; i < result.size(); i++) { - result[i] = tmp[i]; // cast back to T(possibly f16) - } -} - -template -static void multiply_matrices_and_add_a(const std::vector& matrix_a, const std::vector& matrix_b, - std::vector& result, size_t rows_a, size_t cols_a, size_t cols_b) { - // Initialize the result matrix with zero values (f32 accumulator) - std::vector tmp(result.size(), 0.0f); - - // Matrix multiplication logic using linear indexing - for (size_t i = 0; i < rows_a; ++i) { - for (size_t j = 0; j < cols_b; ++j) { - for (size_t k = 0; k < cols_a; ++k) { - tmp[i * cols_b + j] += matrix_a[i * cols_a + k] * matrix_b[k * cols_b + j]; - } - } - } - - for (size_t i = 0; i < result.size(); i++) { - result[i] = tmp[i]; // cast back to T(possibly f16) - } - - for (size_t i = 0; i < matrix_a.size(); i++) { - result[i] += matrix_a[i]; - } -} - -template -static std::vector read_float_array_from_binary_file(const std::string& filename, size_t float_size = 4) { - // Open the binary file in input mode and binary mode - std::ifstream input_file(filename, std::ios::binary); - - // Check if the file was successfully opened - if (!input_file.is_open()) { - std::cerr << "Error: Could not open file " << filename << std::endl; - return {}; - } - - // Move the cursor to the end to determine the size of the file - input_file.seekg(0, std::ios::end); - std::streamsize file_size = input_file.tellg(); - input_file.seekg(0, std::ios::beg); - - // Calculate the number of floats in the file - std::size_t num_floats = file_size / float_size; - - // Create a vector to store the floats - std::vector float_array(num_floats); - - // Read the floats from the file into the vector - if (num_floats > 0) { - input_file.read(reinterpret_cast(float_array.data()), file_size); - } - - // Close the file - input_file.close(); - - return float_array; -} - -template -static ov::Tensor allocate_usm_tensor( - ov::intel_gpu::ocl::ClContext& oclContext, OpenCL* oclInstance, const ov::Shape& shape, - ov::element::Type type, std::vector &input_values) { - cl_int err; - size_t byte_size = shape_size(shape) * type.bitwidth() / 8; - - void* usm_ptr = oclInstance->_usm_helper->allocate_device( - /*properties=*/nullptr, - /*size=*/byte_size, - /*alignment=*/0, - /*err_code_return=*/&err); - std::cout << "allocated: " << usm_ptr << std::endl; - - err = oclInstance->_usm_helper->enqueue_memcpy( - oclInstance->_queue, - /*dst=*/usm_ptr, - /*src=*/input_values.data(), - byte_size, - /*blocking=*/true, - /*wait_list=*/nullptr, - /*ret_event=*/nullptr); - - return oclContext.create_tensor(type, shape, usm_ptr); -} - -template -static ov::Tensor allocate_cl_tensor( - ov::intel_gpu::ocl::ClContext& oclContext, OpenCL* oclInstance, const ov::Shape& shape, - ov::element::Type type, std::vector &input_values, std::vector& keep_alive) { - cl_int err; - size_t byte_size = shape_size(shape) * type.bitwidth() / 8; - - keep_alive.push_back( - cl::Buffer(oclInstance->_context, CL_MEM_READ_WRITE, (cl::size_type)byte_size, NULL, &err)); - - void* mappedPtr = oclInstance->_queue.enqueueMapBuffer(keep_alive.back(), - CL_TRUE, - CL_MAP_WRITE, - 0, - (cl::size_type)byte_size); - - memcpy(mappedPtr, input_values.data(), byte_size); - - oclInstance->_queue.enqueueUnmapMemObject(keep_alive.back(), mappedPtr); - - return oclContext.create_tensor(type, shape, keep_alive.back().get()); -} - -template -static std::vector broadcast_vector(const std::vector& v, size_t new_size) { - std::vector result; - result.reserve(new_size); - - size_t original_size = v.size(); - - if (original_size == 0) { - throw std::invalid_argument("Original vector size must be greater than 0."); - } - - // Fill the result vector by repeating the input vector - for (size_t i = 0; i < new_size; ++i) { - result.push_back(v[i % original_size]); - } - - return result; -} - -// Naive CPU implementation of Scaled Dot-Product Attention for reference results. -// Inputs are 3D: [batch, seq_len, head_dim] for Q/K/V. -// Computes: Output = softmax(Q * K^T * scale) * V -// All intermediate math is done in f32 for accuracy. -template -static std::vector sdpa_ref(const std::vector& Q, - const std::vector& K, - const std::vector& V, - size_t batch, size_t seq_q, size_t head_dim, - size_t seq_k, float scale) { - // Q: [batch, seq_q, head_dim] - // K: [batch, seq_k, head_dim] - // V: [batch, seq_k, head_dim] - // Output: [batch, seq_q, head_dim] - - std::vector output(batch * seq_q * head_dim); - - for (size_t b = 0; b < batch; ++b) { - const size_t q_offset = b * seq_q * head_dim; - const size_t k_offset = b * seq_k * head_dim; - const size_t v_offset = b * seq_k * head_dim; - const size_t o_offset = b * seq_q * head_dim; - - // Step 1: Compute S = Q * K^T * scale -> [seq_q, seq_k] - std::vector S(seq_q * seq_k, 0.0f); - for (size_t i = 0; i < seq_q; ++i) { - for (size_t j = 0; j < seq_k; ++j) { - float dot = 0.0f; - for (size_t d = 0; d < head_dim; ++d) { - dot += static_cast(Q[q_offset + i * head_dim + d]) - * static_cast(K[k_offset + j * head_dim + d]); - } - S[i * seq_k + j] = dot * scale; - } - } - - // Step 2: Row-wise softmax on S - for (size_t i = 0; i < seq_q; ++i) { - // Find row max for numerical stability - float row_max = S[i * seq_k]; - for (size_t j = 1; j < seq_k; ++j) { - row_max = std::max(row_max, S[i * seq_k + j]); - } - // Exponentiate and sum - float row_sum = 0.0f; - for (size_t j = 0; j < seq_k; ++j) { - S[i * seq_k + j] = std::exp(S[i * seq_k + j] - row_max); - row_sum += S[i * seq_k + j]; - } - // Normalize - for (size_t j = 0; j < seq_k; ++j) { - S[i * seq_k + j] /= row_sum; - } - } - - // Step 3: Output = S * V -> [seq_q, head_dim] - for (size_t i = 0; i < seq_q; ++i) { - for (size_t d = 0; d < head_dim; ++d) { - float acc = 0.0f; - for (size_t j = 0; j < seq_k; ++j) { - acc += S[i * seq_k + j] - * static_cast(V[v_offset + j * head_dim + d]); - } - output[o_offset + i * head_dim + d] = static_cast(acc); - } - } - } - - return output; -} - -template -static std::map allocate_input_tensors( - ov::CompiledModel& compiledModel, - std::map> &inputValues, bool use_usm, std::vector& keep_alive) { - auto context = compiledModel.get_context(); - auto& oclContext = static_cast(context); - auto oclInstance = std::make_shared(oclContext.get()); - - std::map input_tensors; - int idx = 0; - for (const auto& input : compiledModel.inputs()) { - auto shape = input.get_shape(); - auto size = ov::shape_size(shape); - std::vector input_values = broadcast_vector(inputValues[idx], size); - ov::Tensor tensor; - if (use_usm) { - tensor = allocate_usm_tensor(oclContext, oclInstance.get(), shape, input.get_element_type(), input_values); - } else { - tensor = allocate_cl_tensor(oclContext, oclInstance.get(), shape, input.get_element_type(), input_values, keep_alive); - } - input_tensors.emplace(idx++, tensor); - } - return input_tensors; -} - -TEST(MLIRExecution, SDPABasic) { - const ov::PartialShape query_shape{4, 4096, 64}; - const ov::PartialShape key_shape{4, 4096, 64}; - const ov::PartialShape value_shape{4, 4096, 64}; - - const auto query = std::make_shared(ov::element::f16, query_shape); - const auto key = std::make_shared(ov::element::f16, key_shape); - const auto value = std::make_shared(ov::element::f16, value_shape); - - const auto casual = false; - const auto sdpa = std::make_shared(query, - key, - value, - casual); - - auto model = std::make_shared(ov::OutputVector{sdpa}, ov::ParameterVector{query, key, value}); - ov::Core core; - - ov::AnyMap device_config; - // disable sdpa-decomposition - device_config[ov::intel_gpu::hint::enable_sdpa_optimization.name()] = true; - - auto compiled_model = core.compile_model(model, "GPU", device_config); - - std::vector keep_alive; - - // Fill Q, K, V with small random values in [-0.5, 0.5] to avoid f16 overflow - const size_t total = 4 * 4096 * 64; - std::mt19937 rng(42); // fixed seed for reproducibility - std::uniform_real_distribution dist(-1.0f, 1.0f); - - auto make_random_f16 = [&](size_t n) { - std::vector v(n); - for (auto& x : v) x = ov::float16(dist(rng)); - return v; - }; - - std::vector matrix_q = make_random_f16(total); - std::vector matrix_k = make_random_f16(total); - std::vector matrix_v = make_random_f16(total); - std::map> input_values_map; - input_values_map.emplace(0, matrix_q); - input_values_map.emplace(1, matrix_k); - input_values_map.emplace(2, matrix_v); - auto input_tensors = allocate_input_tensors(compiled_model, input_values_map, true, keep_alive); - - auto infer_req = compiled_model.create_infer_request(); - for (const auto& input : input_tensors) { - infer_req.set_input_tensor(input.first, input.second); - } - infer_req.infer(); - - auto computed = infer_req.get_output_tensor(0); - ov::float16* result = reinterpret_cast(computed.data()); - - // Compute CPU reference: default scale = 1/sqrt(head_dim) = 1/sqrt(64) = 0.125 - auto reference = sdpa_ref(matrix_q, matrix_k, matrix_v, - /*batch=*/4, /*seq_q=*/4096, /*head_dim=*/64, - /*seq_k=*/4096, /*scale=*/0.125f); - - std::cout << "First 10 reference values: "; - for (size_t i = 0; i < 10; ++i) std::cout << reference[i] << " "; - std::cout << std::endl; - - std::cout << "First 10 result values: "; - for (size_t i = 0; i < 10; ++i) std::cout << result[i] << " "; - std::cout << std::endl; - - // Compare GPU result with the CPU reference using atol + rtol - // f16 SDPA chains matmul→softmax→matmul, so errors compound: - // rtol=1e-2 (f16 has ~3 decimal digits; two matmuls + exp compound) - // atol=1e-3 (handles values near zero where relative error blows up) - const float atol = 1e-3f; - const float rtol = 1e-2f; - for (size_t i = 0; i < reference.size(); ++i) { - float ref = static_cast(reference[i]); - float res = static_cast(result[i]); - float diff = std::abs(ref - res); - float tol = atol + rtol * std::abs(ref); - EXPECT_LE(diff, tol) - << "Mismatch at index " << i - << ": ref=" << ref << " res=" << res - << " diff=" << diff << " tol=" << tol; - } -} - -TEST(MLIRExecution, SimpleMatmulf32) { - ov::Core core; - auto model = core.read_model( - model_full_path("matmul_64_128_f32.xml")); - - ov::AnyMap device_config; - device_config[ov::hint::performance_mode.name()] = ov::hint::PerformanceMode::THROUGHPUT; - device_config[ov::enable_profiling.name()] = false; - device_config.emplace(ov::hint::inference_precision("f32")); - - auto compiled_model = core.compile_model(model, "GPU", device_config); - - std::map> input_values_map; - input_values_map.emplace(0, std::vector(1, 0.5f)); - - std::vector keep_alive; - - auto input_tensors = allocate_input_tensors(compiled_model, input_values_map, true, keep_alive); - - auto infer_req = compiled_model.create_infer_request(); - for (const auto& input : input_tensors) { - infer_req.set_input_tensor(input.first, input.second); - } - infer_req.infer(); - - auto computed = infer_req.get_output_tensor(0); - float* result = reinterpret_cast(computed.data()); - - // compute reference result - std::vector matrix_a = broadcast_vector(input_values_map.at(0), 64 * 128); - std::vector matrix_b = read_float_array_from_binary_file(model_full_path("matmul_64_128_f32.bin")); - ASSERT_EQ(matrix_b.size(), 128 * 128); - std::vector reference_result(64 * 128); - multiply_matrices_and_add_a(matrix_a, matrix_b, reference_result, 64, 128, 128); - - // compare result with the reference - for (size_t i = 0; i < reference_result.size(); ++i) { - EXPECT_NEAR(reference_result[i], result[i], 1e-5); - } -} - -TEST(MLIRExecution, SimpleMatmulf32CLBuffer) { - ov::Core core; - auto model = core.read_model( - model_full_path("matmul_64_128_f32.xml")); - - ov::AnyMap device_config; - device_config[ov::hint::performance_mode.name()] = ov::hint::PerformanceMode::THROUGHPUT; - device_config[ov::enable_profiling.name()] = false; - device_config.emplace(ov::hint::inference_precision("f32")); - - auto compiled_model = core.compile_model(model, "GPU", device_config); - - std::map> input_values_map; - input_values_map.emplace(0, std::vector(1, 0.5f)); - - std::vector keep_alive; - - auto input_tensors = allocate_input_tensors(compiled_model, input_values_map, false, keep_alive); - - auto infer_req = compiled_model.create_infer_request(); - for (const auto& input : input_tensors) { - infer_req.set_input_tensor(input.first, input.second); - } - infer_req.infer(); - - auto computed = infer_req.get_output_tensor(0); - float* result = reinterpret_cast(computed.data()); - - // compute reference result - std::vector matrix_a = broadcast_vector(input_values_map.at(0), 64 * 128); - std::vector matrix_b = read_float_array_from_binary_file(model_full_path("matmul_64_128_f32.bin")); - ASSERT_EQ(matrix_b.size(), 128 * 128); - std::vector reference_result(64 * 128); - multiply_matrices_and_add_a(matrix_a, matrix_b, reference_result, 64, 128, 128); - - // compare result with the reference - for (size_t i = 0; i < reference_result.size(); ++i) { - EXPECT_NEAR(reference_result[i], result[i], 1e-5); - } -} - -TEST(MLIRExecution, SimpleMatmulf16) { - ov::Core core; - auto model = core.read_model( - model_full_path("matmul_64_128_f16.xml")); - - ov::AnyMap device_config; - device_config[ov::hint::performance_mode.name()] = ov::hint::PerformanceMode::THROUGHPUT; - device_config[ov::enable_profiling.name()] = false; - device_config.emplace(ov::hint::inference_precision("f16")); - - auto compiled_model = core.compile_model(model, "GPU", device_config); - - - std::vector keep_alive; - std::vector matrix_a = broadcast_vector(std::vector(1, 1.5), 64 * 128); - std::vector matrix_b = broadcast_vector(std::vector(1, 3.5), 128 * 128); - // std::vector matrix_b = read_float_array_from_binary_file(model_full_path("matmul_64_128_f16.bin"), 2); - std::map> input_values_map; - input_values_map.emplace(0, matrix_a); - input_values_map.emplace(1, matrix_b); - auto input_tensors = allocate_input_tensors(compiled_model, input_values_map, true, keep_alive); - - auto infer_req = compiled_model.create_infer_request(); - for (const auto& input : input_tensors) { - infer_req.set_input_tensor(input.first, input.second); - } - infer_req.infer(); - - auto computed = infer_req.get_output_tensor(0); - ov::float16* result = reinterpret_cast(computed.data()); - - // compute reference result - // ASSERT_EQ(matrix_b.size(), 128 * 128); - std::vector reference_result(64 * 128); - multiply_matrices_and_add_a(matrix_a, matrix_b, reference_result, 64, 128, 128); - - // compare result with the reference - for (size_t i = 0; i < reference_result.size(); ++i) { - EXPECT_NEAR(reference_result[i], result[i], 1e-5); - } -} diff --git a/src/plugins/intel_gpu/tests/common/opencl_helper_instance.hpp b/src/plugins/intel_gpu/tests/unit/test_utils/opencl_helper_instance.hpp similarity index 81% rename from src/plugins/intel_gpu/tests/common/opencl_helper_instance.hpp rename to src/plugins/intel_gpu/tests/unit/test_utils/opencl_helper_instance.hpp index 1577fc4576039f..6963d86e911bd7 100644 --- a/src/plugins/intel_gpu/tests/common/opencl_helper_instance.hpp +++ b/src/plugins/intel_gpu/tests/unit/test_utils/opencl_helper_instance.hpp @@ -23,7 +23,8 @@ struct OpenCL { bool _supports_usm; bool _out_of_order_queue; - OpenCL(bool out_of_order_queue = true) { + OpenCL(bool out_of_order_queue = true) + { // get Intel iGPU OCL device, create context and queue { static constexpr auto INTEL_PLATFORM_VENDOR = "Intel(R) Corporation"; @@ -70,7 +71,8 @@ struct OpenCL { } } - OpenCL(cl::Device device, bool out_of_order_queue = true) { + OpenCL(cl::Device device, bool out_of_order_queue = true) + { cl_uint n = 0; cl_int err = clGetPlatformIDs(0, NULL, &n); checkStatus(err, "clGetPlatformIDs"); @@ -93,20 +95,6 @@ struct OpenCL { _queue = cl::CommandQueue(_context, _device, props); } - OpenCL(cl_context context, bool out_of_order_queue = true) - : _out_of_order_queue(out_of_order_queue) { - _context = cl::Context(context, true); - _device = cl::Device(_context.getInfo()[0].get(), true); - - cl_command_queue_properties props = _out_of_order_queue ? CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE : CL_NONE; - _queue = cl::CommandQueue(_context, _device, props); - - auto extensions = _device.getInfo(); - _supports_usm = extensions.find("cl_intel_unified_shared_memory") != std::string::npos; - - _usm_helper = std::make_shared(_context, _device, _supports_usm); - } - void releaseOclImage(std::shared_ptr image) { checkStatus(clReleaseMemObject(*image), "clReleaseMemObject"); } diff --git a/src/plugins/intel_npu/tools/compile_tool/CMakeLists.txt b/src/plugins/intel_npu/tools/compile_tool/CMakeLists.txt index 5f5a03362d016d..f966cf99ad34e4 100644 --- a/src/plugins/intel_npu/tools/compile_tool/CMakeLists.txt +++ b/src/plugins/intel_npu/tools/compile_tool/CMakeLists.txt @@ -39,7 +39,6 @@ if(CMAKE_COMPILER_IS_GNUCXX OR OV_COMPILER_IS_CLANG) ov_add_compiler_flags(-Wno-missing-declarations) endif() - # # Install # diff --git a/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp b/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp index dd4653a1a7f52f..764b8a4d03a52c 100644 --- a/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp +++ b/src/tests/functional/base_func_tests/include/shared_test_classes/base/benchmark.hpp @@ -151,16 +151,14 @@ class BenchmarkLayerTest : public BaseLayerTest { for (auto& res : results_us) { const std::string node_type_name = res.first; uint64_t& time = res.second; - bool found_profile = false; - for (const auto& profile : profiling_info) { - if (profile.node_type == node_type_name) { - time += profile.real_time.count(); - found_profile = true; - } - } - if (!found_profile) { + auto found_profile = std::find_if(profiling_info.begin(), profiling_info.end(), + [&node_type_name](const ProfilingInfo& profile) { + return profile.node_type == node_type_name; + }); + if (found_profile == profiling_info.end()) { OPENVINO_THROW("Cannot find operator by node type: ", node_type_name); } + time += found_profile->real_time.count(); } } diff --git a/src/tests/functional/plugin/shared/CMakeLists.txt b/src/tests/functional/plugin/shared/CMakeLists.txt index 8d84f8876fce11..6d43f7e9ff84e1 100644 --- a/src/tests/functional/plugin/shared/CMakeLists.txt +++ b/src/tests/functional/plugin/shared/CMakeLists.txt @@ -65,14 +65,6 @@ ov_add_target( LINK_LIBRARIES PUBLIC openvino::pugixml - # paged_attention_token_type.cpp pulls a non-inline ctor from openvino_reference - # (built STATIC, not re-exported from libopenvino.so). Without this explicit dep, - # GNU ld's single-pass static-archive scan hits libopenvino_reference.a before - # libfuncSharedTests.a and skips the ctor -> undefined symbol. Upstream CI doesn't - # see this because it builds funcSharedTests as a shared lib (BUILD_SHARED_LIBS=ON - # default), where reference is linked into the .so up front. Declaring the dep - # here fixes the ordering for the static-funcSharedTests setup. - openvino::reference common_test_utils func_test_utils ov_lpt_models diff --git a/tools/mlir_bench/README.md b/tools/mlir_bench/README.md deleted file mode 100644 index 85dcdb65dccdb3..00000000000000 --- a/tools/mlir_bench/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# MLP benchmarks - -Various MLP benchmarks. -Describes usage of the `*_bench.sh` scripts. - -## LIBXSMM -- F32: -```bash -libxsmm_bench.sh -``` -- BF16: -```bash -libxsmm_bench.sh -B -``` - -## Pure MLIR -- F32: -```bash -tpp_mlir_bench.sh -t f32 -``` -- BF16: -```bash -tpp_mlir_bench.sh -t bf16 -``` - -## OV - no MLIR -Default model:\ -`matmul_transpose_b + bias broadcast` - -Alternative model - scritp flag `-b mlp`:\ -`matmul + bias (no broadcast)` - -- F32: -```bash -OV_MLIR=0 mlp_bench.sh -t f32 -``` -- BF16: -```bash -OV_MLIR=0 mlp_bench.sh -t bf16 -``` - -## OV + MLIR - full -Default model:\ -`matmul_transpose_b + bias broadcast` - -Alternative model - scritp flag `-b mlp`:\ -`matmul + bias (no broadcast)` - -- F32: -```bash -OV_MLIR=1 mlp_bench.sh -t f32 -``` -- BF16: -```bash -OV_MLIR=1 mlp_bench.sh -t bf16 -``` - -## OV + MLIR - kernel only -Default model:\ -`matmul_transpose_b + bias broadcast` - -Alternative model - scritp flag `-b mlp`:\ -`matmul + bias (no broadcast)` - -- F32: -```bash -ov_raw_mlir_bench.sh -t f32 -``` -- BF16: -```bash -ov_raw_mlir_bench.sh -t bf16 -``` diff --git a/tools/mlir_bench/libxsmm_bench.sh b/tools/mlir_bench/libxsmm_bench.sh deleted file mode 100755 index 191a87e48a74e9..00000000000000 --- a/tools/mlir_bench/libxsmm_bench.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2024 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 - -# Runs MLP benchmarks using libxsmm. - -die_syntax() { - echo "Syntax: $0 [-B] [-D] [-l 3]" - echo "" - echo " -B: Use bf16 data type" - echo " -l: Optional number of layers (def:3)" - echo " -D: Set model shapes to dynamic" - exit 1 -} - -# Cmd-line opts -while getopts "l:BD" arg; do - case ${arg} in - B) - DATA_TYPE="bf16" - ;; - D) - IS_DYNAMIC=true - ;; - l) - NUM_LAYERS=${OPTARG} - ;; - ?) - echo "Invalid option: ${OPTARG}" - die_syntax - ;; - esac -done - -BENCH_RUNNER=xsmm_dnn_mlp - -# Initial validation. -if ! [ "$(command -v ${BENCH_RUNNER})" ]; then - echo "Missing benchmark runner ${BENCH_RUNNER}" - exit 1 -fi -if [ "${IS_DYNAMIC}" ]; then - echo "Dynamic shapes are not supported by ${BENCH_RUNNER}" - exit 1 -fi - -# Kernel config. -#LAYERS=( 1024 2048 4096 8192 ) -#MINI_BATCHES=( 128 256 512 ) -LAYERS=( 1024 ) -MINI_BATCHES=( 256 ) -if [ ! "${DATA_TYPE}" ]; then - DATA_TYPE="f32" -fi -if [ ! $NUM_LAYERS ]; then - NUM_LAYERS=3 -fi - -echo "Result type: GFLOPS - NUM LAYERS: ${NUM_LAYERS}" -for MB in "${MINI_BATCHES[@]}"; do - echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" - for LAYER in "${LAYERS[@]}"; do - # Run benchmark. - NUM_ITER=1000 - FUSE_TYPE=5 - TYPE=F - TILES=(64 64 64) - LAYOUT=(0 0) - if [ "${DATA_TYPE}" = "bf16" ]; then - LAYOUT=(1 1) - fi - LAYER_STRING="${LAYER}" - for i in $(seq ${NUM_LAYERS}); do - LAYER_STRING="${LAYER_STRING} ${LAYER}" - done - # Disable parallelism. - ENV_FLAGS=OMP_NUM_THREADS=1 - exec env ${ENV_FLAGS} ${BENCH_RUNNER} ${NUM_ITER} ${MB} ${FUSE_TYPE} ${TYPE} ${TILES[@]} \ - ${LAYOUT[@]} ${LAYER_STRING} \ - | sed -nE "s/.*GFLOPS\s+=\s*([0-9.]+).*/\\1/p" - done -done diff --git a/tools/mlir_bench/lora-runner.xsh b/tools/mlir_bench/lora-runner.xsh deleted file mode 100755 index 8b4bdbe8663835..00000000000000 --- a/tools/mlir_bench/lora-runner.xsh +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env xonsh - -# xonsh can be installed with `pip install xonsh` -# xonsh can then be run by invoking `python -m xonsh` -# this script in particular can be invoked with `python -m xonsh lora-runner.xsh` - -import openvino as ov -from openvino.runtime.op import Constant -from openvino_devtools.builder import OpFactory, outputs_to_nodes -import numpy as np -from pprint import pprint -import re -from os import environ - - -LORA_DIMS = [8, 16, 32, 64, 128] -CONFIGS = [ - [8], [16], [32], [64], [128], [256], [512], [1024], - [2048], [4096], [8192] -] -ITERATIONS = 100 - -BENCH_RUNNER="tpp-run" -RUNNER_FLAGS=f"-entry-point-result=void -e entry -seed 123 -n {ITERATIONS}".split() -DEBUG = environ.get("OV_MLIR_DEBUG", "0").lower() in ("true", "1", "on") - - -def build_ov_lora_model(input_dim=-1, weight_dim=2048, lora_dim=8): - opset = OpFactory('opset13') - - #t40 = opset.Parameter({'shape': [-1, -1, 2048], 'element_type': 'f32'}, output_names=[{'x'}]) # Input data - t40 = opset.Parameter({'shape': [input_dim, weight_dim], 'element_type': 'f32'}, output_names=[{'x'}]) # Input data - t52 = opset.Parameter({'shape': [1, lora_dim], 'element_type': 'f32'}, output_names=[{'alpha'}]) # LoRA alpha parameter - - t48 = Constant(np.random.rand(weight_dim, weight_dim).astype(np.float32)) # -> f32[2048,2048] # Original weight matrix W (usually it is compressed to bf16/f16/u8/u4 and represented as a sub-graph) - t50 = Constant(np.random.rand(lora_dim, weight_dim).astype(np.float32)) # -> f32[8,2048] # LoRA matrix A - t54 = Constant(np.random.rand(weight_dim, lora_dim).astype(np.float32)) # -> f32[2048,8] # LoRA matrix B - - t49 = opset.MatMul([t40, t48], {'transpose_a': False, 'transpose_b': True}) # f32[?,?,2048], f32[2048,2048] -> f32[?,?,2048] - t51 = opset.MatMul([t40, t50], {'transpose_a': False, 'transpose_b': True}) # f32[?,?,2048], f32[8,2048] -> f32[?,?,8] - t53 = opset.Multiply([t51, t52], {'auto_broadcast': 'numpy'}) # f32[?,?,8], f32[1,8] -> f32[?,?,8] - t55 = opset.MatMul([t53, t54], {'transpose_a': False, 'transpose_b': True}) # f32[?,?,8], f32[2048,8] -> f32[?,?,2048] - t56 = opset.Add([t49, t55], {'auto_broadcast': 'numpy'}) # f32[?,?,2048], f32[?,?,2048] -> f32[?,?,2048] - t57 = opset.Result([t56], {}) # f32[?,?,2048] -> f32[?,?,2048] - - parameters = [t40, t52] - results = [t57] - sinks = [] - return ov.Model(outputs_to_nodes(results), outputs_to_nodes(sinks), outputs_to_nodes(parameters)) - - -def build_mlir_lora_model(input_dim=-1, weight_dim=2048, lora_dim=8): - input_dim = '?' if input_dim == -1 else input_dim - mlir_model = f"\ -!inputType = tensor<{input_dim}x{weight_dim}xf32>\n\ -!loraAlphaType = tensor<1x{lora_dim}xf32>\n\ -!weightType = tensor<{weight_dim}x{weight_dim}xf32>\n\ -!loraMatAType = tensor<{lora_dim}x{weight_dim}xf32>\n\ -!loraMatBType = tensor<{weight_dim}x{lora_dim}xf32>\n\ -!loraResultType = tensor<{input_dim}x{lora_dim}xf32>\n\ -func.func @entry(%arg0: !loraAlphaType, %arg1: !inputType) -> !inputType {{\n\ - %cst = arith.constant 0.000000e+00 : f32\n\ - %weights = arith.constant dense<0.001000e+00> : !weightType\n\ - %loraA = arith.constant dense<0.002000e+00> : !loraMatAType\n\ - %loraB = arith.constant dense<0.003000e+00> : !loraMatBType\n\ - %0 = tensor.empty() : !loraResultType\n\ - %1 = linalg.fill ins(%cst : f32) outs(%0 : !loraResultType)\ - -> !loraResultType\n\ - %2 = linalg.matmul_transpose_b ins(%arg1, %loraA : !inputType, !loraMatAType)\ - outs(%1 : !loraResultType) -> !loraResultType\n\ - %collapsed = tensor.collapse_shape %arg0 [[0, 1]] : !loraAlphaType into tensor<{lora_dim}xf32>\n\ - %broadcasted = linalg.broadcast ins(%collapsed : tensor<{lora_dim}xf32>)\ - outs(%0 : !loraResultType) dimensions = [0]\n\ - %3 = linalg.mul ins(%2, %broadcasted : !loraResultType, !loraResultType)\ - outs(%0 : !loraResultType) -> !loraResultType\n\ - %4 = tensor.empty() : !inputType\n\ - %5 = linalg.fill ins(%cst : f32) outs(%4 : !inputType) -> !inputType\n\ - %6 = linalg.matmul_transpose_b ins(%3, %loraB : !loraResultType, !loraMatBType)\ - outs(%5 : !inputType) -> !inputType\n\ - %7 = linalg.matmul_transpose_b ins(%arg1, %weights : !inputType, !weightType)\ - outs(%5 : !inputType) -> !inputType\n\ - %8 = linalg.add ins(%7, %6 : !inputType, !inputType) outs(%4 : !inputType) -> !inputType\n\ - return %8 : !inputType\n\ -}}\n\ -" - return mlir_model - - -def full_run(lora_dim): - no_mlir_averages = [] - mlir_averages = [] - no_ov_averages = [] - manual_mlir_averages = [] - for config in CONFIGS: - model_desc = '.'.join(str(x) for x in config) - model_xml = f"lora.{model_desc}.xml" - model = build_ov_lora_model(*config, lora_dim=lora_dim) - ov.save_model(model, model_xml) - - BENCH_FLAGS=f"-m {model_xml} -d CPU -ip f32 -infer_precision f32 -hint none -nstreams 1 -nthreads 1".split() - - def run_ov(env_str): - out = $(env @(env_str.split()) benchmark_app @(BENCH_FLAGS) -niter @(ITERATIONS)) - match = re.search(r"Median: +(\d.*) ms", out) - return float(match.group(1)) - no_mlir_averages.append(run_ov("OV_MLIR=0")) - mlir_averages.append(run_ov("OV_MLIR=1")) - - def run_no_ov_mlir(env_str): - raw_kernel_secs = $(env @(env_str.split()) benchmark_app @(BENCH_FLAGS) -niter 1 2>&1 | awk '/Source MLIR:/{flag=1; next} /Target LLVM:/{flag=0} flag' | grep -vE '^[-]+$' | tpp-run @(RUNNER_FLAGS)) - return float(raw_kernel_secs) * 1000 - no_ov_averages.append(run_no_ov_mlir("OV_MLIR=1 OV_MLIR_TPP=1 OV_MLIR_DEBUG=1")) - - def run_manual_mlir(env_str): - mlir_model = build_mlir_lora_model(*config, lora_dim=lora_dim) - if DEBUG: - print(mlir_model) - raw_kernel_secs = $(@(lambda: print(mlir_model)) | tpp-run @(RUNNER_FLAGS) --lower-pack-unpack-without-transpose) - return float(raw_kernel_secs) * 1000 - manual_mlir_averages.append(run_manual_mlir("")) - - print("CONFIGS", CONFIGS) - print("OV NO-MLIR", no_mlir_averages) - print("OV MLIR", mlir_averages) - print("NO-OV MLIR", list(round(x, 2) for x in no_ov_averages)) - print("MANUAL MLIR", list(round(x, 2) for x in manual_mlir_averages)) - - -def main(): - for lora_dim in LORA_DIMS: - print("lora_dim =", lora_dim) - full_run(lora_dim) - - -if __name__ == "__main__": - main() diff --git a/tools/mlir_bench/mlp_bench.sh b/tools/mlir_bench/mlp_bench.sh deleted file mode 100755 index 2317607d20c662..00000000000000 --- a/tools/mlir_bench/mlp_bench.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2024 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 - -# Runs OV MLP benchmarks. - -die_syntax() { - echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-b (mlp)] [-D] [-l 3]" - echo "" - echo " -t: Optional data type" - echo " -b: Optional baseline model" - echo " -l: Optional number of layers (def:3)" - echo " -D: Set model shapes to dynamic" - exit 1 -} - -# Cmd-line opts -while getopts "t:l:b:D" arg; do - case ${arg} in - t) - DATA_TYPE=${OPTARG} - ;; - b) - BASELINE_MODEL=${OPTARG} - ;; - l) - NUM_LAYERS=${OPTARG} - ;; - D) - IS_DYNAMIC=true - ;; - ?) - echo "Invalid option: ${OPTARG}" - die_syntax - ;; - esac -done - -if [ ! $NUM_LAYERS ]; then - NUM_LAYERS=3 -fi - -OV_ROOT=$(git rev-parse --show-toplevel) -BENCH_ROOT=$(realpath "${OV_ROOT}/tools/mlir_bench") - -MODEL_GEN=$(realpath "${BENCH_ROOT}/ov_model_gen.py") -BENCH_RUNNER=benchmark_app - -# Initial validation. -if ! [ -d "${OV_ROOT}" ]; then - echo "Missing OV repo" - exit 1 -fi -if ! [ -d "${BENCH_ROOT}" ]; then - echo "Missing MLIR benchmark directory" - exit 1 -fi -if ! [ -f "${MODEL_GEN}" ]; then - echo "Missing model generator" - exit 1 -fi -if ! [ "$(command -v ${BENCH_RUNNER})" ]; then - echo "Missing benchmark runner ${BENCH_RUNNER}" - exit 1 -fi -if [ "${BASELINE_MODEL}" ] && [ "${IS_DYNAMIC}" ]; then - echo "Baseline models with dynamic shapes not supported" - exit 1 -fi - -# Kernel config. -#LAYERS=( 1024 2048 4096 8192 ) -#MINI_BATCHES=( 128 256 512 ) -LAYERS=( 1024 ) -MINI_BATCHES=( 256 ) -if [ ! "${DATA_TYPE}" ]; then - DATA_TYPE="f32" -fi -MODEL_NAME="MLIR_MLP_BENCH.xml" - -echo "Result type: time [ms] - NUM LAYERS: ${NUM_LAYERS}" -for MB in "${MINI_BATCHES[@]}"; do - echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" - for LAYER in "${LAYERS[@]}"; do - # Generate model. - if [ "${BASELINE_MODEL}" ]; then - # Enable baseline model flag. - MODEL_CONFIG=(-b="${BASELINE_MODEL}[${MB},${LAYER},${LAYER}]x${NUM_LAYERS}") - else - # Generate default PyTorch MLP. - LAYER_STRING="linear[${MB},${LAYER},${LAYER}] relu[]" - for i in $(seq ${NUM_LAYERS}); do - MODEL_STRING="${MODEL_STRING}${LAYER_STRING} " - done - MODEL_CONFIG=(-l="${MODEL_STRING}") - fi - echo "MODEL_CONFIG=${MODEL_CONFIG}" - GEN_FLAGS=(-t ${DATA_TYPE} -n ${MODEL_NAME}) - if [ "${IS_DYNAMIC}" ]; then - GEN_FLAGS+=(--dynamic) - fi - python3 ${MODEL_GEN} "${MODEL_CONFIG[@]}" "${GEN_FLAGS[@]}" - if [ $? != 0 ]; then - echo "Failed to generate model" - exit 1 - fi - # Run benchmark. - PRECISION=${DATA_TYPE} - if [ "${DATA_TYPE}" = "bf16" ]; then - # No native support for bf16, use simple f16 instead. - PRECISION="f16" - fi - if [ "${IS_DYNAMIC}" ]; then - DATA_SHAPE=(-data_shape [${MB},${LAYER}]) - fi - # Benchmark config. Disable parallelism. - PERF_FLAGS="-niter 1000 -hint none -nstreams 1 -nthreads 1" - BENCH_FLAGS="-m ${MODEL_NAME} -d CPU -ip ${PRECISION} -infer_precision ${DATA_TYPE} ${DATA_SHAPE[@]} ${PERF_FLAGS}" - ${BENCH_RUNNER} ${BENCH_FLAGS} 2>/dev/null | \ - sed -nE "s/.*\[ INFO \]\s*Median:\s*([0-9.]+).*/\\1/p" - done -done diff --git a/tools/mlir_bench/ov_model_gen.py b/tools/mlir_bench/ov_model_gen.py deleted file mode 100644 index f233bac14edd2d..00000000000000 --- a/tools/mlir_bench/ov_model_gen.py +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/python3 - -# Copyright (C) 2024 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 - -from __future__ import annotations -import argparse -import string -import sys -import os - -import torch -import torch.nn as nn -import openvino as ov - - -class TorchAdd(nn.Module): - def __init__(self, sizes, type=None): - super().__init__() - # Generate random data - self.tensor = torch.empty(*sizes, dtype=type).data.normal_(0, 0.01) - def forward(self, a): - return a + self.tensor - - -class TorchSub(nn.Module): - def __init__(self, sizes, type=None): - super().__init__() - # Generate random data - self.tensor = torch.empty(*sizes, dtype=type).data.normal_(0, 0.01) - def forward(self, a): - return a - self.tensor - - -class TorchMul(nn.Module): - def __init__(self, sizes, type=None): - super().__init__() - # Generate random data - self.tensor = torch.empty(*sizes, dtype=type).data.normal_(0, 0.01) - def forward(self, a): - return a * self.tensor - - -class TorchMatmul(nn.Module): - def __init__(self, sizes_mnk, type=None): - super().__init__() - k = sizes_mnk[2] - n = sizes_mnk[1] - # Generate random data - self.weights = torch.empty(k, n, dtype=type).data.normal_(0, 0.01) - def forward(self, a): - return torch.matmul(a, self.weights) - - -class TorchDiv(nn.Module): - def __init__(self, sizes, type=None): - super().__init__() - # Generate random weights - self.tensor = torch.empty(*sizes, dtype=type).data.normal_(1, 10) - def forward(self, a): - return a / self.tensor - - -class TorchSequential(nn.Module): - def __init__(self): - super(TorchSequential, self).__init__() - self.model = nn.Sequential() - def forward(self, a): - return self.model(a) - def append(self, module: nn.Module): - self.model.append(module) - - -def get_torch_type(type: str) -> torch.dtype: - if type == 'f32': - return torch.float32 - if type == 'f16': - return torch.float16 - if type == 'bf16': - return torch.bfloat16 - assert False, f"Unsupported torch data type {type}" - - -def get_torch_layer(layer: str, sizes: list[int], type: str) -> nn.Module: - data_type = get_torch_type(type) - if layer == 'linear': - assert len(sizes) == 3, "invalid sizes for linear - expects [m,n,k]" - linear = nn.Linear(sizes[2], sizes[1], dtype=data_type) - # Generate random weights - linear.weight.data.normal_(0, 0.01) - linear.bias.data.fill_(0.01) - return linear - if layer == 'relu': - return nn.ReLU() - if layer == 'add': - return TorchAdd(sizes, data_type) - if layer == 'sub': - return TorchSub(sizes, data_type) - if layer == 'mul': - return TorchMul(sizes, data_type) - if layer == 'div': - return TorchDiv(sizes, data_type) - if layer == 'matmul': - assert len(sizes) == 3, "invalid sizes for mm - expects [m,n,k]" - return TorchMatmul(sizes, data_type) - assert False, f"Unsupported torch layer type {layer}" - - -def get_layer_name(layer_desc: str) -> str: - return layer_desc[0:layer_desc.find('[')] - - -def get_layer_sizes(layer_desc: str) -> list[int]: - desc_sizes = layer_desc[layer_desc.find('[')+1:layer_desc.find(']')] - return [int(size) for size in filter(None, desc_sizes.split(','))] - - -def get_layer_num_layers(layer_desc: str) -> int: - layers = layer_desc[layer_desc.find('x')+1:] - return int(layers) - - -def parse_layer(layer_desc: str, type: str) -> nn.Module: - layer = get_layer_name(layer_desc) - sizes = get_layer_sizes(layer_desc) - return get_torch_layer(layer, sizes, type) - - -def get_ov_type(type: str) -> ov.Type: - if type == 'f32': - return ov.Type.f32 - if type == 'f16': - return ov.Type.f16 - if type == 'bf16': - return ov.Type.bf16 - assert False, f"Unsupported OV data type {type}" - - -def get_layer_inputs(layer_desc: str, is_dynamic: bool): - input_sizes = get_layer_sizes(layer_desc) - if is_dynamic: - input_sizes = [-1] * len(input_sizes) - - layer = get_layer_name(layer_desc) - - if layer == 'matmul' or layer == 'linear': - m = input_sizes[0] - k = input_sizes[2] - return [m,k] - - return input_sizes - - -def generate_ov_model(layers_desc: str, data_type: str, file_name: str, - is_dynamic: bool = False): - layers = layers_desc.split() - torch_seq = TorchSequential() - for layer in layers: - module = parse_layer(layer, data_type) - torch_seq.append(module) - - input_sizes = get_layer_sizes(layers[0]) - if len(input_sizes) == 0: - print("Invalid input layer sizes") - sys.exit(1) - - input_shapes = get_layer_inputs(layers[0], is_dynamic) - ov_type = get_ov_type(data_type) - inputs = (ov.PartialShape(input_shapes), ov_type) - - ov_model = ov.convert_model(torch_seq, input=inputs) - ov.save_model(ov_model, f"{file_name}") - return ov_model - - -class BaselineMLP(nn.Module): - def __init__(self, sizes_mnk, type=None, layers=3): - super(BaselineMLP, self).__init__() - m = sizes_mnk[0] - n = sizes_mnk[1] - self.bias = torch.empty((m, n), dtype=type).data.fill_(0.01) - self.relu = nn.ReLU() - self.layers = layers - def forward(self, a, b): - for _ in range(0,self.layers): - c = torch.matmul(a, b) - c = torch.add(c, self.bias) - a = self.relu(c) - return a - - -def baseline_MLP(model_desc: str, data_type: str, is_dynamic: bool) -> tuple[nn.Model, list]: - sizes = get_layer_sizes(model_desc) - assert len(sizes) == 3, "Invalid baseline MLP sizes" - layers = get_layer_num_layers(model_desc) - if (layers is None): - layers = 3 # Default to 3 layers - mlp = BaselineMLP(sizes, get_torch_type(data_type), layers) - input_shapes = get_layer_inputs(model_desc, is_dynamic) - m = input_shapes[0] - n = input_shapes[1] - k = input_shapes[2] - ov_type = get_ov_type(data_type) - inputs = [(ov.PartialShape([m, k]), ov_type), (ov.PartialShape([k, n]), ov_type)] - return (mlp, inputs) - - -def generate_baseline_model(model_desc: str, data_type: str, file_name: str, is_dynamic: bool = False): - model_name = get_layer_name(model_desc) - - if model_name == 'mlp': - baseline_tuple = baseline_MLP(model_desc, data_type, is_dynamic) - else: - assert False, f"Unsupported baseline model data type {model_name}" - - ov_model = ov.convert_model(baseline_tuple[0], input=baseline_tuple[1]) - ov.save_model(ov_model, f"{file_name}") - return ov_model - - -def main(): - parser = argparse.ArgumentParser( - prog='OV Model generator', - description='Generate PyTorch model and export as OV .xml') - parser.add_argument('-l', '--layers', type=str.lower, - help='Model layers description. For example:\ - -l="linear[128,1024,256] relu[] linear[128,512,1024] gelu[]"\ - -l="matmul[128,128,1024] add[128,128] relu[]"\ - -l="add[8,8] div[8,8]"') - parser.add_argument('-t', '--type', default='f32', type=str.lower, - help='Data type: f32|f16|bf16|...') - parser.add_argument('--dynamic', action='store_true', - help='Make model shapes dynamic') - parser.add_argument('-n', '--name', default='temp.xml', - help='Name for exported XML model') - parser.add_argument('-b', '--baseline', default=None, type=str.lower, - help='Baseline pre-made model - overrides layers. For example:\ - -b=mlp[32,64,16]x10') - parser.add_argument('-p', '--print', action='store_true', - help='Compile and print the model') - args = parser.parse_args() - - if args.baseline is not None: - model = generate_baseline_model(args.baseline, args.type, args.name, args.dynamic) - else: - model = generate_ov_model(args.layers, args.type, args.name, args.dynamic) - - if args.print: - ov.compile_model(model, 'CPU') - - return 0 - - -if __name__ == '__main__': - os._exit(main()) diff --git a/tools/mlir_bench/ov_raw_mlir_bench.sh b/tools/mlir_bench/ov_raw_mlir_bench.sh deleted file mode 100755 index dd11b91ff810c3..00000000000000 --- a/tools/mlir_bench/ov_raw_mlir_bench.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2024 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 - -# Runs pure MLIR part of MLP benchmarks using TPP-MLIR. -# This approach assumes that only one MLIR op is generated. -# For example, the whole graph is outlined to MLIR. - -die_syntax() { - echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-b (mlp)] [-D] [-l 3]" - echo "" - echo " -t: Optional data type" - echo " -b: Optional baseline model" - echo " -l: Optional number of layers (def:3)" - echo " -D: Set model shapes to dynamic" - exit 1 -} - -# Cmd-line opts -while getopts "t:b:l:D" arg; do - case ${arg} in - t) - DATA_TYPE=${OPTARG} - ;; - b) - BASELINE_MODEL=${OPTARG} - ;; - l) - NUM_LAYERS=${OPTARG} - ;; - D) - IS_DYNAMIC=true - ;; - ?) - echo "Invalid option: ${OPTARG}" - die_syntax - ;; - esac -done - -OV_ROOT=$(git rev-parse --show-toplevel) -BENCH_ROOT=$(realpath ${OV_ROOT}/tools/mlir_bench) - -MODEL_GEN=$(realpath ${BENCH_ROOT}/ov_model_gen.py) -BENCH_RUNNER=tpp-run - -# Initial validation. -if ! [ -d ${OV_ROOT} ]; then - echo "Missing OV repo" - exit 1 -fi -if ! [ -d ${BENCH_ROOT} ]; then - echo "Missing MLIR benchmark directory" - exit 1 -fi -if ! [ -f ${MODEL_GEN} ]; then - echo "Missing model generator" - exit 1 -fi -if ! [ "$(command -v ${BENCH_RUNNER})" ]; then - echo "Missing benchmark runner ${BENCH_RUNNER}" - exit 1 -fi -if [ "${IS_DYNAMIC}" ]; then - echo "Dynamic shapes are not supported by ${BENCH_RUNNER}" - exit 1 -fi - -# Kernel config. -# LAYERS=( 1024 2048 4096 8192 ) -# MINI_BATCHES=( 128 256 512 ) -LAYERS=( 1024 ) -MINI_BATCHES=( 256 ) -if [ ! "${DATA_TYPE}" ]; then - DATA_TYPE="f32" -fi -if [ ! $NUM_LAYERS ]; then - NUM_LAYERS=3 -fi -MODEL_NAME="TPP_BENCH.xml" - -echo "Result type: time [s] - NUM LAYERS: ${NUM_LAYERS}" -for MB in "${MINI_BATCHES[@]}"; do - echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" - for LAYER in "${LAYERS[@]}"; do - # Generate model. - if [ "${BASELINE_MODEL}" ]; then - # Enable baseline model flag. - MODEL_CONFIG=(-b="${BASELINE_MODEL}[${MB},${LAYER},${LAYER}]x${NUM_LAYERS}") - else - # Generate default PyTorch MLP. - LAYER_STRING="linear[${MB},${LAYER},${LAYER}] relu[]" - for i in $(seq ${NUM_LAYERS}); do - MODEL_STRING="${MODEL_STRING}${LAYER_STRING} " - done - MODEL_CONFIG=(-l="${MODEL_STRING}") - fi - echo "MODEL_CONFIG=${MODEL_CONFIG}" - GEN_FLAGS=(-t ${DATA_TYPE} -n ${MODEL_NAME}) - GEN_FLAGS+=(-p) - ENV_FLAGS="OV_MLIR_TPP=0 OV_MLIR_DEBUG=1" - MODEL_OUT=$(exec env ${ENV_FLAGS} python3 ${MODEL_GEN} "${MODEL_CONFIG[@]}" "${GEN_FLAGS[@]}" 2>&1) - if [ $? != 0 ]; then - echo "Failed to generate model" - exit 1 - fi - # Run benchmark. - MLIR_IR=$(echo "${MODEL_OUT}" \ - | awk '/Source MLIR:/{flag=1; next} /Target LLVM:/{flag=0} flag' \ - | grep -vE '^[-]+$') - BENCH_FLAGS="-entry-point-result=void -e entry -seed 123 -n 1000" - echo "${MLIR_IR}" | ${BENCH_RUNNER} ${BENCH_FLAGS} - done -done diff --git a/tools/mlir_bench/run_bench_bf16.sh b/tools/mlir_bench/run_bench_bf16.sh deleted file mode 100755 index a03c0f3d35a163..00000000000000 --- a/tools/mlir_bench/run_bench_bf16.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -die_syntax() { - echo "Syntax: $0 [-l 3]" - echo "" - echo " -l: Optional number of layers (def: 3)" - exit 1 -} - -# Cmd-line opts -while getopts "l:" arg; do - case ${arg} in - l) - NUM_LAYERS=${OPTARG} - ;; - ?) - echo "Invalid option: ${OPTARG}" - die_syntax - ;; - esac -done - -if [ ! "${NUM_LAYERS}" ]; then - NUM_LAYERS=3 -fi - -export OV_MLIR_DEBUG=1 - -echo "### MLP BF16 benchmarks ###" -echo "# Layers: ${NUM_LAYERS} #" -echo "LIBXSMM" -../tools/mlir_bench/libxsmm_bench.sh -B -l ${NUM_LAYERS} -echo "TPP-MLIR args weights" -../tools/mlir_bench/tpp_mlir_bench.sh -t bf16 -l ${NUM_LAYERS} - -echo "" -echo "Baseline MLP" -echo "OV - no MLIR - baseline model" -OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t bf16 -b mlp -l ${NUM_LAYERS} -echo "OV + MLIR - kernel only - baseline model" -../tools/mlir_bench/ov_raw_mlir_bench.sh -t bf16 -b mlp -l ${NUM_LAYERS} -echo "OV + MLIR - full - baseline model" -OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t bf16 -b mlp -l ${NUM_LAYERS} - -echo "" -echo "PyTorch MLP" -echo "OV - no MLIR - PyTorch" -OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t bf16 -l ${NUM_LAYERS} -echo "OV + MLIR - kernel only - PyTorch" -../tools/mlir_bench/ov_raw_mlir_bench.sh -t bf16 -l ${NUM_LAYERS} -echo "OV + MLIR - full - PyTorch" -OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t bf16 -l ${NUM_LAYERS} - -echo "TPP-MLIR const weights" -../tools/mlir_bench/tpp_mlir_bench.sh -t bf16 -C -l ${NUM_LAYERS} diff --git a/tools/mlir_bench/run_bench_f32.sh b/tools/mlir_bench/run_bench_f32.sh deleted file mode 100755 index 19c5125d6138cc..00000000000000 --- a/tools/mlir_bench/run_bench_f32.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -die_syntax() { - echo "Syntax: $0 [-l 3]" - echo "" - echo " -l: Optional number of layers (def: 3)" - exit 1 -} - -# Cmd-line opts -while getopts "l:" arg; do - case ${arg} in - l) - NUM_LAYERS=${OPTARG} - ;; - ?) - echo "Invalid option: ${OPTARG}" - die_syntax - ;; - esac -done - -if [ ! "${NUM_LAYERS}" ]; then - NUM_LAYERS=3 -fi - -export OV_MLIR_DEBUG=1 - -echo "### MLP F32 benchmarks ###" -echo "# Layers: ${NUM_LAYERS} #" -echo "LIBXSMM" -../tools/mlir_bench/libxsmm_bench.sh -l ${NUM_LAYERS} -echo "TPP-MLIR args weights" -../tools/mlir_bench/tpp_mlir_bench.sh -t f32 -l ${NUM_LAYERS} - -echo "" -echo "Baseline MLP" -echo "OV - no MLIR - baseline model" -OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t f32 -b mlp -l ${NUM_LAYERS} -echo "OV + MLIR - kernel only - baseline model" -../tools/mlir_bench/ov_raw_mlir_bench.sh -t f32 -b mlp -l ${NUM_LAYERS} -echo "OV + MLIR - full - baseline model" -OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t f32 -b mlp -l ${NUM_LAYERS} - -echo "" -echo "PyTorch MLP" -echo "OV - no MLIR - PyTorch" -OV_MLIR=0 ../tools/mlir_bench/mlp_bench.sh -t f32 -l ${NUM_LAYERS} -echo "OV + MLIR - kernel only - PyTorch" -../tools/mlir_bench/ov_raw_mlir_bench.sh -t f32 -l ${NUM_LAYERS} -echo "OV + MLIR - full - PyTorch" -OV_MLIR=1 ../tools/mlir_bench/mlp_bench.sh -t f32 -l ${NUM_LAYERS} - -echo "TPP-MLIR const weights" -../tools/mlir_bench/tpp_mlir_bench.sh -t f32 -C -l ${NUM_LAYERS} diff --git a/tools/mlir_bench/tpp_mlir_bench.sh b/tools/mlir_bench/tpp_mlir_bench.sh deleted file mode 100755 index 0b2da819ecb478..00000000000000 --- a/tools/mlir_bench/tpp_mlir_bench.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2024 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 - -# Runs MLIR only MLP benchmarks using TPP-MLIR. - -die_syntax() { - echo "Syntax: $0 [-t (f32|f16|bf16|...)] [-D] [-C] [-l 3]" - echo "" - echo " -t: Optional data type" - echo " -l: Optional number of layers (def:3)" - echo " -D: Set model shapes to dynamic" - echo " -C: Weights as constants (default: arguments)" - exit 1 -} - -# Cmd-line opts -while getopts "t:l:DC" arg; do - case ${arg} in - t) - DATA_TYPE=${OPTARG} - ;; - l) - NUM_LAYERS=${OPTARG} - ;; - D) - IS_DYNAMIC=true - ;; - C) - CONST_WEIGHTS=true - ;; - ?) - echo "Invalid option: ${OPTARG}" - die_syntax - ;; - esac -done - -MODEL_GEN=mlir-gen -BENCH_RUNNER=tpp-run - -# Initial validation. -if ! [ "$(command -v ${MODEL_GEN})" ]; then - echo "Missing model generator ${MODEL_GEN}" - exit 1 -fi -if ! [ "$(command -v ${BENCH_RUNNER})" ]; then - echo "Missing benchmark runner ${BENCH_RUNNER}" - exit 1 -fi -if [ "${IS_DYNAMIC}" ]; then - echo "Dynamic shapes are not supported by ${BENCH_RUNNER}" - exit 1 -fi - -# Kernel config. -# LAYERS=( 1024 2048 4096 8192 ) -# MINI_BATCHES=( 128 256 512 ) -LAYERS=( 1024 ) -MINI_BATCHES=( 256 ) -if [ ! "${DATA_TYPE}" ]; then - DATA_TYPE="f32" -fi -if [ ! $NUM_LAYERS ]; then - NUM_LAYERS=3 -fi - -echo "Result type: time [s] - NUM LAYERS: ${NUM_LAYERS}" -for MB in "${MINI_BATCHES[@]}"; do - echo "MLP - MB: ${MB} LAYERS: ${LAYERS[@]}" - for LAYER in "${LAYERS[@]}"; do - # Generate model. - LAYER_STRING="${LAYER}" - for i in $(seq ${NUM_LAYERS}); do - LAYER_STRING="${LAYER_STRING},${LAYER}" - done - MODEL_CONFIG=(--batch=${MB} --layers=${LAYER_STRING} -bias -relu) - KERNEL_TYPE=args - if [ "${CONST_WEIGHTS}" ]; then - KERNEL_TYPE=const - fi - GEN_FLAGS=(--kernel=${KERNEL_TYPE} --float-type=${DATA_TYPE} --seed=123) - MLIR_IR=$(${MODEL_GEN} "${MODEL_CONFIG[@]}" "${GEN_FLAGS[@]}") - if [ $? != 0 ]; then - echo "Failed to generate model" - exit 1 - fi - # Run benchmark. - BENCH_FLAGS="-entry-point-result=void -e entry -seed 123 -n 1000" - echo "${MLIR_IR}" | ${BENCH_RUNNER} ${BENCH_FLAGS} - done -done From f70d93892bddeb3bc8230e568bdadb39e9be2d76 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Fri, 24 Jul 2026 11:48:41 +0000 Subject: [PATCH 088/121] revert pvc support in ci Signed-off-by: dchigarev --- .github/workflows/graph-compiler.yml | 103 ++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/.github/workflows/graph-compiler.yml b/.github/workflows/graph-compiler.yml index 56d17f9175b1ed..6fa21d69ce5aeb 100644 --- a/.github/workflows/graph-compiler.yml +++ b/.github/workflows/graph-compiler.yml @@ -21,7 +21,108 @@ jobs: - uses: actions/checkout@v7 with: submodules: recursive - + + - name: Apply GPU revert patch + run: | + cat > /tmp/revert.patch <<'PATCH_EOF' + diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp + --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp + +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp + @@ -44,9 +44,10 @@ + const uint64_t LARGE_OUTPUT_BYTES_THRESHOLD = 4 * 1048576; + + const auto& device_info = engine.get_device_info(); + - if ((device_info.gfx_ver.major >= 20 && device_info.dev_type == cldnn::device_type::discrete_gpu) || + + if ((device_info.gfx_ver.major == 12 && device_info.gfx_ver.minor == 60) || + + (device_info.gfx_ver.major >= 20 && device_info.dev_type == cldnn::device_type::discrete_gpu) || + (device_info.dev_type == cldnn::device_type::discrete_gpu && total_output_bytes > LARGE_OUTPUT_BYTES_THRESHOLD)) { + - // WA: Disable USM host memory for infer request`s tensors for dGPUs, as kernel access + + // WA: Disable USM host memory for infer request`s tensors for PVC and subsequent dGPUs, as kernel access + // to system memory is slower than using an explicit memcpy (Host <-> Device) call with the copy engine + // Driver tickets with additional details: 6155, 10054 + GPU_DEBUG_TRACE << "Do not use usm_host for performance issue" << std::endl; + diff --git a/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp b/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp + --- a/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp + +++ b/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp + @@ -1533,7 +1533,10 @@ + case gpu_arch::xe_hpg: { + config = choose_config_xehpg(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_paged_attention, is_prefill); + break; + } + + case gpu_arch::xe_hpc: + + config = choose_config_xehpc(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_integrated, is_paged_attention, is_prefill); + + break; + case gpu_arch::xe2: + case gpu_arch::xe3: + config = choose_config_xe2(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_integrated, is_paged_attention, is_prefill); + diff --git a/src/plugins/intel_gpu/src/runtime/device.cpp b/src/plugins/intel_gpu/src/runtime/device.cpp + --- a/src/plugins/intel_gpu/src/runtime/device.cpp + +++ b/src/plugins/intel_gpu/src/runtime/device.cpp + @@ -101,6 +101,8 @@ + { { {12, 0, 0}, {12, 9, MAX_REVISION} }, { 0, 16, 32, 0, 0, 64 }, {} }, // TGL, RKL, ADL + { { {12, 10, 0} }, { 0, 16, 32, 0, 0, 64 }, {} }, // DG1 + { { {12, 55, 0}, {12, 57, MAX_REVISION} }, { 0, 16, 32, 128, 256, 0 }, {} }, // DG2 + + { { {12, 60, 0}, {12, 60, 1} }, { 16, 32, 64, 512, 1024, 0 }, {} }, // PVC_XL + + { { {12, 60, 3}, {12, 61, 7} }, { 32, 32, 64, 512, 1024, 0 }, {} }, // PVC_XT + { { {12, 70, 0}, {12, 71, MAX_REVISION} }, { 0.5, 16, 32, 0, 0, 64 }, {} }, // MTL/ARL-S + { { {12, 74, 0}, {12, 74, MAX_REVISION} }, { 0.5, 16, 32, 128, 256, 0 }, {} }, // ARL-H + { { {20, 1, 0}, {20, 2, MAX_REVISION} }, { 1, 16, 32, 128, 256, 0 }, {} }, // BMG + diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp + --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp + +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp + @@ -60,7 +60,7 @@ + case ngen::HW::XeLP: return gpu_arch::xe_lp; + case ngen::HW::XeHP: return gpu_arch::xe_hp; + case ngen::HW::XeHPG: return gpu_arch::xe_hpg; + - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); + + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; + case ngen::HW::Xe2: return gpu_arch::xe2; + case ngen::HW::Xe3: return gpu_arch::xe3; + case ngen::HW::Xe3p: return gpu_arch::xe3p; + diff --git a/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp b/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp + --- a/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp + +++ b/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp + @@ -64,7 +64,7 @@ + case ngen::HW::XeLP: return gpu_arch::xe_lp; + case ngen::HW::XeHP: return gpu_arch::xe_hp; + case ngen::HW::XeHPG: return gpu_arch::xe_hpg; + - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); + + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; + case ngen::HW::Xe2: return gpu_arch::xe2; + case ngen::HW::Xe3: return gpu_arch::xe3; + case ngen::HW::Xe3p: return gpu_arch::xe3p; + diff --git a/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp b/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp + --- a/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp + +++ b/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp + @@ -45,7 +45,7 @@ + case ngen::HW::XeLP: return gpu_arch::xe_lp; + case ngen::HW::XeHP: return gpu_arch::xe_hp; + case ngen::HW::XeHPG: return gpu_arch::xe_hpg; + - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); + + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; + case ngen::HW::Xe2: return gpu_arch::xe2; + case ngen::HW::Xe3: return gpu_arch::xe3; + case ngen::HW::Xe3p: return gpu_arch::xe3p; + diff --git a/src/plugins/intel_gpu/thirdparty/CMakeLists.txt b/src/plugins/intel_gpu/thirdparty/CMakeLists.txt + --- a/src/plugins/intel_gpu/thirdparty/CMakeLists.txt + +++ b/src/plugins/intel_gpu/thirdparty/CMakeLists.txt + @@ -14,6 +14,6 @@ + set(ONEDNN_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_build") + set(ONEDNN_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_install" CACHE PATH "Installation path for oneDNN GPU library") + set(ONEDNN_PREFIX_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_root") + - set(ONEDNN_ENABLED_PRIMITIVES "CONCAT;CONVOLUTION;DECONVOLUTION;GATED_MLP;MATMUL;REORDER;POOLING;REDUCTION;RNN") + - set(ONEDNN_ENABLED_ISA "XELP;XEHP;XEHPG;XE2;XE3;XE3P") + + set(ONEDNN_ENABLED_PRIMITIVES "CONCAT;CONVOLUTION;DECONVOLUTION;GATED_MLP;INNER_PRODUCT;MATMUL;REORDER;POOLING;REDUCTION;SDPA;RNN") + + set(ONEDNN_ENABLED_ISA "ALL") + set(DNNL_GPU_LIBRARY_NAME "openvino_onednn_gpu" CACHE STRING "Name of oneDNN library for Intel GPU Plugin") + PATCH_EOF + # Idempotent: skip if the patch is already applied (e.g. reused workspace). + if git -C "$GITHUB_WORKSPACE" apply --reverse --check /tmp/revert.patch 2>/dev/null; then + echo "Revert patch already applied, skipping." + else + git -C "$GITHUB_WORKSPACE" apply -v /tmp/revert.patch + fi + - name: Build run: | mkdir -p $BUILD_DIR From 03c0a8aab4e1b0405c40bd6b30c649d9fd9bf9b5 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Fri, 24 Jul 2026 12:12:53 +0000 Subject: [PATCH 089/121] use git revert Signed-off-by: dchigarev --- .github/workflows/graph-compiler.yml | 103 ++------------------------- 1 file changed, 4 insertions(+), 99 deletions(-) diff --git a/.github/workflows/graph-compiler.yml b/.github/workflows/graph-compiler.yml index 6fa21d69ce5aeb..2e9327b116e91e 100644 --- a/.github/workflows/graph-compiler.yml +++ b/.github/workflows/graph-compiler.yml @@ -21,107 +21,12 @@ jobs: - uses: actions/checkout@v7 with: submodules: recursive + fetch-depth: 0 - - name: Apply GPU revert patch + - name: Revert GPU oneDNN commit run: | - cat > /tmp/revert.patch <<'PATCH_EOF' - diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp - --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp - +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp - @@ -44,9 +44,10 @@ - const uint64_t LARGE_OUTPUT_BYTES_THRESHOLD = 4 * 1048576; - - const auto& device_info = engine.get_device_info(); - - if ((device_info.gfx_ver.major >= 20 && device_info.dev_type == cldnn::device_type::discrete_gpu) || - + if ((device_info.gfx_ver.major == 12 && device_info.gfx_ver.minor == 60) || - + (device_info.gfx_ver.major >= 20 && device_info.dev_type == cldnn::device_type::discrete_gpu) || - (device_info.dev_type == cldnn::device_type::discrete_gpu && total_output_bytes > LARGE_OUTPUT_BYTES_THRESHOLD)) { - - // WA: Disable USM host memory for infer request`s tensors for dGPUs, as kernel access - + // WA: Disable USM host memory for infer request`s tensors for PVC and subsequent dGPUs, as kernel access - // to system memory is slower than using an explicit memcpy (Host <-> Device) call with the copy engine - // Driver tickets with additional details: 6155, 10054 - GPU_DEBUG_TRACE << "Do not use usm_host for performance issue" << std::endl; - diff --git a/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp b/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp - --- a/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp - +++ b/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp - @@ -1533,7 +1533,10 @@ - case gpu_arch::xe_hpg: { - config = choose_config_xehpg(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_paged_attention, is_prefill); - break; - } - + case gpu_arch::xe_hpc: - + config = choose_config_xehpc(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_integrated, is_paged_attention, is_prefill); - + break; - case gpu_arch::xe2: - case gpu_arch::xe3: - config = choose_config_xe2(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_integrated, is_paged_attention, is_prefill); - diff --git a/src/plugins/intel_gpu/src/runtime/device.cpp b/src/plugins/intel_gpu/src/runtime/device.cpp - --- a/src/plugins/intel_gpu/src/runtime/device.cpp - +++ b/src/plugins/intel_gpu/src/runtime/device.cpp - @@ -101,6 +101,8 @@ - { { {12, 0, 0}, {12, 9, MAX_REVISION} }, { 0, 16, 32, 0, 0, 64 }, {} }, // TGL, RKL, ADL - { { {12, 10, 0} }, { 0, 16, 32, 0, 0, 64 }, {} }, // DG1 - { { {12, 55, 0}, {12, 57, MAX_REVISION} }, { 0, 16, 32, 128, 256, 0 }, {} }, // DG2 - + { { {12, 60, 0}, {12, 60, 1} }, { 16, 32, 64, 512, 1024, 0 }, {} }, // PVC_XL - + { { {12, 60, 3}, {12, 61, 7} }, { 32, 32, 64, 512, 1024, 0 }, {} }, // PVC_XT - { { {12, 70, 0}, {12, 71, MAX_REVISION} }, { 0.5, 16, 32, 0, 0, 64 }, {} }, // MTL/ARL-S - { { {12, 74, 0}, {12, 74, MAX_REVISION} }, { 0.5, 16, 32, 128, 256, 0 }, {} }, // ARL-H - { { {20, 1, 0}, {20, 2, MAX_REVISION} }, { 1, 16, 32, 128, 256, 0 }, {} }, // BMG - diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp - --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp - +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp - @@ -60,7 +60,7 @@ - case ngen::HW::XeLP: return gpu_arch::xe_lp; - case ngen::HW::XeHP: return gpu_arch::xe_hp; - case ngen::HW::XeHPG: return gpu_arch::xe_hpg; - - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); - + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; - case ngen::HW::Xe2: return gpu_arch::xe2; - case ngen::HW::Xe3: return gpu_arch::xe3; - case ngen::HW::Xe3p: return gpu_arch::xe3p; - diff --git a/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp b/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp - --- a/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp - +++ b/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp - @@ -64,7 +64,7 @@ - case ngen::HW::XeLP: return gpu_arch::xe_lp; - case ngen::HW::XeHP: return gpu_arch::xe_hp; - case ngen::HW::XeHPG: return gpu_arch::xe_hpg; - - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); - + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; - case ngen::HW::Xe2: return gpu_arch::xe2; - case ngen::HW::Xe3: return gpu_arch::xe3; - case ngen::HW::Xe3p: return gpu_arch::xe3p; - diff --git a/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp b/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp - --- a/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp - +++ b/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp - @@ -45,7 +45,7 @@ - case ngen::HW::XeLP: return gpu_arch::xe_lp; - case ngen::HW::XeHP: return gpu_arch::xe_hp; - case ngen::HW::XeHPG: return gpu_arch::xe_hpg; - - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); - + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; - case ngen::HW::Xe2: return gpu_arch::xe2; - case ngen::HW::Xe3: return gpu_arch::xe3; - case ngen::HW::Xe3p: return gpu_arch::xe3p; - diff --git a/src/plugins/intel_gpu/thirdparty/CMakeLists.txt b/src/plugins/intel_gpu/thirdparty/CMakeLists.txt - --- a/src/plugins/intel_gpu/thirdparty/CMakeLists.txt - +++ b/src/plugins/intel_gpu/thirdparty/CMakeLists.txt - @@ -14,6 +14,6 @@ - set(ONEDNN_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_build") - set(ONEDNN_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_install" CACHE PATH "Installation path for oneDNN GPU library") - set(ONEDNN_PREFIX_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_root") - - set(ONEDNN_ENABLED_PRIMITIVES "CONCAT;CONVOLUTION;DECONVOLUTION;GATED_MLP;MATMUL;REORDER;POOLING;REDUCTION;RNN") - - set(ONEDNN_ENABLED_ISA "XELP;XEHP;XEHPG;XE2;XE3;XE3P") - + set(ONEDNN_ENABLED_PRIMITIVES "CONCAT;CONVOLUTION;DECONVOLUTION;GATED_MLP;INNER_PRODUCT;MATMUL;REORDER;POOLING;REDUCTION;SDPA;RNN") - + set(ONEDNN_ENABLED_ISA "ALL") - set(DNNL_GPU_LIBRARY_NAME "openvino_onednn_gpu" CACHE STRING "Name of oneDNN library for Intel GPU Plugin") - PATCH_EOF - # Idempotent: skip if the patch is already applied (e.g. reused workspace). - if git -C "$GITHUB_WORKSPACE" apply --reverse --check /tmp/revert.patch 2>/dev/null; then - echo "Revert patch already applied, skipping." - else - git -C "$GITHUB_WORKSPACE" apply -v /tmp/revert.patch - fi + COMMIT=b7d9ce6e9110e6cbbf22106270ea3932fda547e3 + git revert --no-commit $COMMIT - name: Build run: | From 802f39050eec607c1e680ea4f7ccd9c7af8e8c6f Mon Sep 17 00:00:00 2001 From: dchigarev Date: Fri, 24 Jul 2026 12:20:55 +0000 Subject: [PATCH 090/121] Revert "use git revert" This reverts commit 03c0a8aab4e1b0405c40bd6b30c649d9fd9bf9b5. --- .github/workflows/graph-compiler.yml | 103 +++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 4 deletions(-) diff --git a/.github/workflows/graph-compiler.yml b/.github/workflows/graph-compiler.yml index 2e9327b116e91e..6fa21d69ce5aeb 100644 --- a/.github/workflows/graph-compiler.yml +++ b/.github/workflows/graph-compiler.yml @@ -21,12 +21,107 @@ jobs: - uses: actions/checkout@v7 with: submodules: recursive - fetch-depth: 0 - - name: Revert GPU oneDNN commit + - name: Apply GPU revert patch run: | - COMMIT=b7d9ce6e9110e6cbbf22106270ea3932fda547e3 - git revert --no-commit $COMMIT + cat > /tmp/revert.patch <<'PATCH_EOF' + diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp + --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp + +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp + @@ -44,9 +44,10 @@ + const uint64_t LARGE_OUTPUT_BYTES_THRESHOLD = 4 * 1048576; + + const auto& device_info = engine.get_device_info(); + - if ((device_info.gfx_ver.major >= 20 && device_info.dev_type == cldnn::device_type::discrete_gpu) || + + if ((device_info.gfx_ver.major == 12 && device_info.gfx_ver.minor == 60) || + + (device_info.gfx_ver.major >= 20 && device_info.dev_type == cldnn::device_type::discrete_gpu) || + (device_info.dev_type == cldnn::device_type::discrete_gpu && total_output_bytes > LARGE_OUTPUT_BYTES_THRESHOLD)) { + - // WA: Disable USM host memory for infer request`s tensors for dGPUs, as kernel access + + // WA: Disable USM host memory for infer request`s tensors for PVC and subsequent dGPUs, as kernel access + // to system memory is slower than using an explicit memcpy (Host <-> Device) call with the copy engine + // Driver tickets with additional details: 6155, 10054 + GPU_DEBUG_TRACE << "Do not use usm_host for performance issue" << std::endl; + diff --git a/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp b/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp + --- a/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp + +++ b/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp + @@ -1533,7 +1533,10 @@ + case gpu_arch::xe_hpg: { + config = choose_config_xehpg(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_paged_attention, is_prefill); + break; + } + + case gpu_arch::xe_hpc: + + config = choose_config_xehpc(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_integrated, is_paged_attention, is_prefill); + + break; + case gpu_arch::xe2: + case gpu_arch::xe3: + config = choose_config_xe2(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_integrated, is_paged_attention, is_prefill); + diff --git a/src/plugins/intel_gpu/src/runtime/device.cpp b/src/plugins/intel_gpu/src/runtime/device.cpp + --- a/src/plugins/intel_gpu/src/runtime/device.cpp + +++ b/src/plugins/intel_gpu/src/runtime/device.cpp + @@ -101,6 +101,8 @@ + { { {12, 0, 0}, {12, 9, MAX_REVISION} }, { 0, 16, 32, 0, 0, 64 }, {} }, // TGL, RKL, ADL + { { {12, 10, 0} }, { 0, 16, 32, 0, 0, 64 }, {} }, // DG1 + { { {12, 55, 0}, {12, 57, MAX_REVISION} }, { 0, 16, 32, 128, 256, 0 }, {} }, // DG2 + + { { {12, 60, 0}, {12, 60, 1} }, { 16, 32, 64, 512, 1024, 0 }, {} }, // PVC_XL + + { { {12, 60, 3}, {12, 61, 7} }, { 32, 32, 64, 512, 1024, 0 }, {} }, // PVC_XT + { { {12, 70, 0}, {12, 71, MAX_REVISION} }, { 0.5, 16, 32, 0, 0, 64 }, {} }, // MTL/ARL-S + { { {12, 74, 0}, {12, 74, MAX_REVISION} }, { 0.5, 16, 32, 128, 256, 0 }, {} }, // ARL-H + { { {20, 1, 0}, {20, 2, MAX_REVISION} }, { 1, 16, 32, 128, 256, 0 }, {} }, // BMG + diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp + --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp + +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp + @@ -60,7 +60,7 @@ + case ngen::HW::XeLP: return gpu_arch::xe_lp; + case ngen::HW::XeHP: return gpu_arch::xe_hp; + case ngen::HW::XeHPG: return gpu_arch::xe_hpg; + - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); + + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; + case ngen::HW::Xe2: return gpu_arch::xe2; + case ngen::HW::Xe3: return gpu_arch::xe3; + case ngen::HW::Xe3p: return gpu_arch::xe3p; + diff --git a/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp b/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp + --- a/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp + +++ b/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp + @@ -64,7 +64,7 @@ + case ngen::HW::XeLP: return gpu_arch::xe_lp; + case ngen::HW::XeHP: return gpu_arch::xe_hp; + case ngen::HW::XeHPG: return gpu_arch::xe_hpg; + - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); + + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; + case ngen::HW::Xe2: return gpu_arch::xe2; + case ngen::HW::Xe3: return gpu_arch::xe3; + case ngen::HW::Xe3p: return gpu_arch::xe3p; + diff --git a/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp b/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp + --- a/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp + +++ b/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp + @@ -45,7 +45,7 @@ + case ngen::HW::XeLP: return gpu_arch::xe_lp; + case ngen::HW::XeHP: return gpu_arch::xe_hp; + case ngen::HW::XeHPG: return gpu_arch::xe_hpg; + - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); + + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; + case ngen::HW::Xe2: return gpu_arch::xe2; + case ngen::HW::Xe3: return gpu_arch::xe3; + case ngen::HW::Xe3p: return gpu_arch::xe3p; + diff --git a/src/plugins/intel_gpu/thirdparty/CMakeLists.txt b/src/plugins/intel_gpu/thirdparty/CMakeLists.txt + --- a/src/plugins/intel_gpu/thirdparty/CMakeLists.txt + +++ b/src/plugins/intel_gpu/thirdparty/CMakeLists.txt + @@ -14,6 +14,6 @@ + set(ONEDNN_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_build") + set(ONEDNN_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_install" CACHE PATH "Installation path for oneDNN GPU library") + set(ONEDNN_PREFIX_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_root") + - set(ONEDNN_ENABLED_PRIMITIVES "CONCAT;CONVOLUTION;DECONVOLUTION;GATED_MLP;MATMUL;REORDER;POOLING;REDUCTION;RNN") + - set(ONEDNN_ENABLED_ISA "XELP;XEHP;XEHPG;XE2;XE3;XE3P") + + set(ONEDNN_ENABLED_PRIMITIVES "CONCAT;CONVOLUTION;DECONVOLUTION;GATED_MLP;INNER_PRODUCT;MATMUL;REORDER;POOLING;REDUCTION;SDPA;RNN") + + set(ONEDNN_ENABLED_ISA "ALL") + set(DNNL_GPU_LIBRARY_NAME "openvino_onednn_gpu" CACHE STRING "Name of oneDNN library for Intel GPU Plugin") + PATCH_EOF + # Idempotent: skip if the patch is already applied (e.g. reused workspace). + if git -C "$GITHUB_WORKSPACE" apply --reverse --check /tmp/revert.patch 2>/dev/null; then + echo "Revert patch already applied, skipping." + else + git -C "$GITHUB_WORKSPACE" apply -v /tmp/revert.patch + fi - name: Build run: | From 5e7ba788dc4744ed9c54ee83dd44415d45668405 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Fri, 24 Jul 2026 12:37:02 +0000 Subject: [PATCH 091/121] Fix copyrights Signed-off-by: dchigarev --- src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp | 2 +- .../plugin/transformations/mlir/common/conversion_context.cpp | 2 +- .../plugin/transformations/mlir/common/conversion_context.hpp | 2 +- .../src/plugin/transformations/mlir/common/convert_common.cpp | 2 +- .../src/plugin/transformations/mlir/common/convert_common.hpp | 2 +- .../transformations/mlir/common/converters/binary_eltwise.hpp | 2 +- .../plugin/transformations/mlir/common/converters/concat.hpp | 2 +- .../src/plugin/transformations/mlir/common/converters/floor.hpp | 2 +- .../plugin/transformations/mlir/common/converters/gather.hpp | 2 +- .../plugin/transformations/mlir/common/converters/matmul.hpp | 2 +- .../plugin/transformations/mlir/common/converters/reduce.hpp | 2 +- .../src/plugin/transformations/mlir/common/converters/relu.hpp | 2 +- .../plugin/transformations/mlir/common/converters/reshape.hpp | 2 +- .../src/plugin/transformations/mlir/common/converters/sdpa.hpp | 2 +- .../plugin/transformations/mlir/common/converters/shape_of.hpp | 2 +- .../src/plugin/transformations/mlir/common/converters/slice.hpp | 2 +- .../plugin/transformations/mlir/common/converters/squeeze.hpp | 2 +- .../plugin/transformations/mlir/common/converters/transpose.hpp | 2 +- .../transformations/mlir/common/converters/unary_eltwise.hpp | 2 +- .../plugin/transformations/mlir/common/converters/unsqueeze.hpp | 2 +- .../src/plugin/transformations/mlir/common/typedefs.hpp | 2 +- .../src/plugin/transformations/mlir/conversion/patterns.cpp | 2 +- .../src/plugin/transformations/mlir/conversion/patterns.hpp | 2 +- .../intel_gpu/src/plugin/transformations/mlir/convert.cpp | 2 +- .../src/plugin/transformations/mlir/graph_converter.cpp | 2 +- .../src/plugin/transformations/mlir/graph_converter.hpp | 2 +- .../src/plugin/transformations/mlir/subgraph_tracker.cpp | 2 +- .../src/plugin/transformations/mlir/subgraph_tracker.hpp | 2 +- 28 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp index 7f5c41c01717a1..8d15ab21ff90e8 100644 --- a/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp +++ b/src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023-2026 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.cpp index d74c079cedaf1a..1c1331f7cc1126 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.hpp index c054df92d94bbc..8bcb7ef9601ce4 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/conversion_context.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp index 056bee530a4a02..08e2b099521a45 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp index ad28e7670c163a..e5cdc89741108c 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp index d309b8712a22f0..f210ddff82701e 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp index 6b5ae999becb8a..a4b8574d999396 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/floor.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/floor.hpp index 2d45d8b9921778..9e87b6c2d2d8fc 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/floor.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/floor.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp index 6093a586758756..b85f29f94d2c6f 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp index 8b5aa3185446eb..431116d9a78159 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp index 7baad6ce64d140..ca4b404cb4bc77 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp index 9d591fd26ced21..a94e3f2cf23133 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp index cc105980565884..53e1c0d2380ea6 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/sdpa.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/sdpa.hpp index 63d6ceaac9e530..7c34c6db568b9c 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/sdpa.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/sdpa.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/shape_of.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/shape_of.hpp index 0f5d5a27a7fc0e..1972e5bc8b4ba7 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/shape_of.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/shape_of.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp index 4719b1988031f5..f618e61f9a8e5c 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp index be1c374d6ef3f7..af6a2a40b80cc8 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/transpose.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/transpose.hpp index 245087cf7bac06..8b2b4f63688113 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/transpose.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/transpose.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unary_eltwise.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unary_eltwise.hpp index 6827d19908ca98..b7603b46b0f7cf 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unary_eltwise.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unary_eltwise.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unsqueeze.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unsqueeze.hpp index 0b2ffc28a25c86..90dbc284666152 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unsqueeze.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/unsqueeze.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/typedefs.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/typedefs.hpp index 697684d37f1a93..ad526d9862525c 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/typedefs.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/typedefs.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp index a898fbaa584273..eb93eba792a441 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp index 89c1d989cd186e..ccb62d2f347517 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp index 87c5af37c1288a..d0a324797ee95b 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp index de5b47ec8735db..357dc4ea54eb60 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp index 4ad5ce9b984017..179e1b3c2ecfa7 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp index 02df629883bbf2..ebd6e169e166d2 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp index b09bbf869fd199..26b1f6819d0176 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 Intel Corporation +// Copyright (C) 2018-2026 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // From 3b4811301baafbc45039e4b69f4e3074090b775e Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Fri, 24 Jul 2026 15:46:32 +0200 Subject: [PATCH 092/121] Bump docker tag (#16) Signed-off-by: dchigarev --- .github/dockerfiles/docker_tag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dockerfiles/docker_tag b/.github/dockerfiles/docker_tag index 2f5295787269f9..5a5a6672dddd06 100644 --- a/.github/dockerfiles/docker_tag +++ b/.github/dockerfiles/docker_tag @@ -1 +1 @@ -pr-36049 +pr-35336 From 723fc6578ef3dedbe90cbbd3e5d856fbb9555632 Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Fri, 24 Jul 2026 17:34:39 +0200 Subject: [PATCH 093/121] Fix ov-ci (#17) * Fix for l0 runtime Signed-off-by: dchigarev * fix codestyle Signed-off-by: dchigarev --------- Signed-off-by: dchigarev --- install_build_dependencies.sh | 2 +- .../intel_gpu/include/intel_gpu/runtime/event.hpp | 2 +- .../intel_gpu/include/intel_gpu/runtime/memory.hpp | 2 +- .../intel_gpu/include/intel_gpu/runtime/stream.hpp | 5 +++-- .../intel_gpu/src/graph/impls/common/mlir_primitive.cpp | 8 ++++---- src/plugins/intel_gpu/src/runtime/ocl/ocl_base_event.hpp | 2 +- src/plugins/intel_gpu/src/runtime/ocl/ocl_memory.hpp | 2 +- src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.cpp | 9 ++++++--- src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.hpp | 5 +++-- 9 files changed, 21 insertions(+), 16 deletions(-) diff --git a/install_build_dependencies.sh b/install_build_dependencies.sh index 8adb22a1a6c55d..4880d3534c46e3 100755 --- a/install_build_dependencies.sh +++ b/install_build_dependencies.sh @@ -94,7 +94,7 @@ if [ -f /etc/lsb-release ] || [ -f /etc/debian_version ] ; then # LLVM/MLIR nightly from apt.llvm.org for arg in "$@"; do if [ "$arg" = "-llvm" ]; then - : ${LLVM_VERSION:=$(grep -Po '(?<=set\(SUPPORTED_LLVM_VERSION ")[^"]*' "$(dirname "$0")/cmake/llvm.cmake")} + : "${LLVM_VERSION:=$(grep -Po '(?<=set\(SUPPORTED_LLVM_VERSION ")[^"]*' "$(dirname "$0")/cmake/llvm.cmake")}" if ! dpkg -l "libmlir-${LLVM_VERSION}-dev" &>/dev/null; then wget -qO- https://apt.llvm.org/llvm.sh | bash -s -- "${LLVM_VERSION}" all apt-get install -y --no-install-recommends \ diff --git a/src/plugins/intel_gpu/include/intel_gpu/runtime/event.hpp b/src/plugins/intel_gpu/include/intel_gpu/runtime/event.hpp index 3a6afeac41a5f3..20e2e631003b50 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/runtime/event.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/runtime/event.hpp @@ -46,7 +46,7 @@ struct event { // returns true if handler has been successfully added bool add_event_handler(event_handler handler, void* data); // return a handle to an underlying event implementation (i.e. cl_event for OpenCL) - virtual void* get_handle() { return nullptr; } + virtual void* get_native_handle() { return nullptr; } std::vector get_profiling_info(); diff --git a/src/plugins/intel_gpu/include/intel_gpu/runtime/memory.hpp b/src/plugins/intel_gpu/include/intel_gpu/runtime/memory.hpp index 0ec7420eefb48d..f7b9d30709304e 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/runtime/memory.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/runtime/memory.hpp @@ -56,7 +56,7 @@ struct memory { // only supports gpu_usm virtual void* buffer_ptr() const { return nullptr; } // Returns the handle to the underlying memory object (e.g. cl_mem for OpenCL) - virtual void* get_handle() const { return nullptr; } + virtual void* get_native_handle() const { return nullptr; } size_t size() const { return _bytes_count; } size_t count() const { return _layout.count(); } diff --git a/src/plugins/intel_gpu/include/intel_gpu/runtime/stream.hpp b/src/plugins/intel_gpu/include/intel_gpu/runtime/stream.hpp index 31d63222bc02c6..d7469c439c3849 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/runtime/stream.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/runtime/stream.hpp @@ -66,13 +66,14 @@ class stream { virtual event::ptr group_events(std::vector const& deps) = 0; virtual void wait_for_events(const std::vector& events) = 0; virtual event::ptr create_user_event(bool set) = 0; - virtual event::ptr create_base_event(void* handle = nullptr) = 0; + virtual event::ptr create_base_event() = 0; + virtual event::ptr create_base_event(void* /*handle*/) { return nullptr; } virtual std::unique_ptr create_surfaces_lock(const std::vector &mem) const = 0; virtual event::ptr aggregate_events(const std::vector& events, bool group = false, bool is_output = false); QueueTypes get_queue_type() const { return m_queue_type; } // Returns the handle to the underlying stream object (e.g. cl_command_queue for OpenCL) - virtual void* get_handle() const { return nullptr; } + virtual void* get_native_handle() const { return nullptr; } SyncMethods get_sync_method() const { return m_sync_method; } static SyncMethods get_expected_sync_method(const ExecutionConfig& config); diff --git a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp index aa8920317c4b5a..d487da62d98857 100644 --- a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp +++ b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp @@ -52,7 +52,7 @@ struct mlir_primitive_impl : typed_primitive_impl { auto process_buffer = [&is_usm_ptr](memory::ptr mem, ov::TensorVector& tensors) { switch (mem->get_allocation_type()) { case allocation_type::cl_mem: { - if (void* cl_buff = mem->get_handle()) { + if (void* cl_buff = mem->get_native_handle()) { tensors.push_back(make_tensor(mem->get_layout(), cl_buff)); is_usm_ptr.push_back(false); } else { @@ -91,7 +91,7 @@ struct mlir_primitive_impl : typed_primitive_impl { } ov::EvaluationContext meta; - if (void* queue = stream.get_handle()) { + if (void* queue = stream.get_native_handle()) { meta.insert(ov::intel_gpu::ocl_queue(queue)); } else { OPENVINO_THROW("Unsupported queue type"); @@ -113,7 +113,7 @@ struct mlir_primitive_impl : typed_primitive_impl { if (!ev) { continue; } - if (void* cl_ev = ev->get_handle()) { + if (void* cl_ev = ev->get_native_handle()) { events_list.push_back(cl_ev); } else { depends.push_back(ev); @@ -121,7 +121,7 @@ struct mlir_primitive_impl : typed_primitive_impl { } if (!depends.empty()) { marker = stream.enqueue_marker(depends, true); - if (void* cl_ev = marker->get_handle()) { + if (void* cl_ev = marker->get_native_handle()) { events_list.push_back(cl_ev); } } diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_base_event.hpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_base_event.hpp index 179efe5cffd7cd..c358030cc14b75 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_base_event.hpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_base_event.hpp @@ -26,7 +26,7 @@ struct ocl_base_event : public event { explicit ocl_base_event(uint64_t queue_stamp = 0) : event(), _queue_stamp(queue_stamp) { } uint64_t get_queue_stamp() const { return _queue_stamp; } virtual cl::Event& get() = 0; - void* get_handle() override { return static_cast(get().get()); } + void* get_native_handle() override { return static_cast(get().get()); } protected: uint64_t _queue_stamp = 0; diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_memory.hpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_memory.hpp index ca40322f6c1a36..9aaf65003c7c42 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_memory.hpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_memory.hpp @@ -47,7 +47,7 @@ struct gpu_buffer : public lockable_gpu_mem, public memory { void* buffer_ptr() const override { return get_buffer().get(); } - void* get_handle() const override { return static_cast(get_buffer().get()); } + void* get_native_handle() const override { return static_cast(get_buffer().get()); } event::ptr copy_from(stream& stream, const void* data_ptr, size_t src_offset, size_t dst_offset, size_t size, bool blocking) override; diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.cpp index 9cd6fb3e47d23d..5c1a49b1b15d24 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.cpp @@ -372,11 +372,14 @@ event::ptr ocl_stream::create_user_event(bool set) { return std::make_shared(_engine.get_cl_context(), set, _profiling_device); } +event::ptr ocl_stream::create_base_event() { + cl::Event ret_ev; + return std::make_shared(ret_ev, ++_queue_counter); +} + event::ptr ocl_stream::create_base_event(void* handle) { cl::Event ret_ev; - if (handle) { - ret_ev = reinterpret_cast(handle); - } + ret_ev = reinterpret_cast(handle); return std::make_shared(ret_ev, ++_queue_counter); } diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.hpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.hpp index 28f5958b53ad0d..6feb27a5fa1f09 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.hpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_stream.hpp @@ -18,7 +18,7 @@ namespace ocl { class ocl_stream : public stream { public: const ocl_queue_type& get_cl_queue() const { return _command_queue; } - void* get_handle() const override { return static_cast(get_cl_queue().get()); } + void* get_native_handle() const override { return static_cast(get_cl_queue().get()); } ocl_stream(const ocl_engine& engine, const ExecutionConfig& config); ocl_stream(const ocl_engine &engine, const ExecutionConfig& config, void *handle); @@ -48,7 +48,8 @@ class ocl_stream : public stream { void wait_for_events(const std::vector& events) override; void enqueue_barrier() override; event::ptr create_user_event(bool set) override; - event::ptr create_base_event(void* handle = nullptr) override; + event::ptr create_base_event() override; + event::ptr create_base_event(void* handle) override; std::unique_ptr create_surfaces_lock(const std::vector &mem) const override; const cl::UsmHelper& get_usm_helper() const { return _engine.get_usm_helper(); } From 8d27aa436894d4f5fe362ce49e106f475e653f3d Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Fri, 24 Jul 2026 00:50:37 +0000 Subject: [PATCH 094/121] Implemented graph matcher The patterns are specified with the env var: OV_MLIR_PATTERNS="name1=Type1,Type2;name2=Type3,Type4,...". --- .github/workflows/graph-compiler.yml | 4 + src/plugins/intel_gpu/src/plugin/plugin.cpp | 1 + .../mlir/common/convert_common.cpp | 3 +- .../mlir/common/convert_common.hpp | 2 +- .../plugin/transformations/mlir/convert.cpp | 168 ++++++++++++++++-- .../transformations/mlir/graph_converter.cpp | 5 +- .../transformations/mlir/mlir_evaluate.cpp | 7 + .../transformations/mlir/subgraph_tracker.cpp | 12 +- .../transformations/mlir/subgraph_tracker.hpp | 4 +- .../mlir_op/matmul_rms_norm_concat.cpp | 7 +- .../functional/single_layer_tests/reduce.cpp | 8 +- 11 files changed, 187 insertions(+), 34 deletions(-) diff --git a/.github/workflows/graph-compiler.yml b/.github/workflows/graph-compiler.yml index 6fa21d69ce5aeb..7ef96c30e62484 100644 --- a/.github/workflows/graph-compiler.yml +++ b/.github/workflows/graph-compiler.yml @@ -174,3 +174,7 @@ jobs: start=$SECONDS echo "$tests" | xargs -P 32 -I{} "$func_tests" --gtest_filter='{}' echo "Run $(echo "$tests" | wc -l) tests in $((SECONDS - start))s" + + OV_MLIR_PATTERNS='mart=MatMul,Add,Reshape,Transpose;rms=Power,ReduceMean,Add,Sqrt,Divide' \ + OV_MLIR_DEBUG=1 "$func_tests" --gtest_filter=mlir_MatMulRmsnormConcatTest* 2>&1 \ + | grep -E 'func.func @(mart|rms)' | wc -l | xargs test 4 -eq || { echo "MLIR patterns test failed" && exit 1; } diff --git a/src/plugins/intel_gpu/src/plugin/plugin.cpp b/src/plugins/intel_gpu/src/plugin/plugin.cpp index b175559db3a964..0a0f40584eeb19 100644 --- a/src/plugins/intel_gpu/src/plugin/plugin.cpp +++ b/src/plugins/intel_gpu/src/plugin/plugin.cpp @@ -788,6 +788,7 @@ std::vector Plugin::get_supported_properties() const { ov::PropertyName{ov::hint::model.name(), PropertyMutability::WO}, ov::PropertyName{ov::intel_gpu::offload_ratio.name(), PropertyMutability::RW}, ov::PropertyName{ov::intel_gpu::config_file.name(), PropertyMutability::RW}, + ov::PropertyName{ov::intel_gpu::enable_mlir.name(), PropertyMutability::RW}, }; return supported_properties; diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp index 08e2b099521a45..2f9ab1ccf48f05 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp @@ -199,7 +199,8 @@ bool statically_broadcastable(const PartialShape& from, const PartialShape& to) } bool is_debug() { - return util::getenv_bool("OV_MLIR_DEBUG", false); + static bool debug = util::getenv_bool("OV_MLIR_DEBUG", false); + return debug; } } // namespace ov::intel_gpu::mlir \ No newline at end of file diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp index e5cdc89741108c..65ab9bc950f5c5 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.hpp @@ -25,7 +25,7 @@ using namespace ::mlir; bool is_debug(); #define OPENVINO_MLIR_DEBUG(X) do if(::ov::intel_gpu::mlir::is_debug()) { X; } while(false) -#define OPENVINO_MLIR_DEBUG_PRINT(X) do if(::ov::intel_gpu::mlir::is_debug()) { ::std::cerr << X; } while(false) +#define OPENVINO_MLIR_DEBUG_PRINT(X) do if(::ov::intel_gpu::mlir::is_debug()) { ::std::cerr << "[DEBUG] " << X << ::std::endl; } while(false) Location createLayerLocation(MLIRContext* ctx, const std::string& layerName, const std::string& layerType); diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp index d0a324797ee95b..04fee0d7a417d3 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp @@ -30,6 +30,7 @@ #include #include #include +#include // TODO: Prune unused headers -- it's hard to understand needed ones #include "graph_converter.hpp" @@ -169,7 +170,9 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, auto memref_args = inputTypes; memref_args.append(outputTypes); const auto funcType = mlir::FunctionType::get(context, ArrayRef(memref_args), ArrayRef(SmallVector())); - auto func = moduleBuilder.create(funcLoc, function_name, funcType); + auto trunc = function_name.rfind('#'); + auto name = trunc == std::string::npos ? function_name : function_name.substr(0, trunc); + auto func = mlir::func::FuncOp::create(moduleBuilder, funcLoc, name, funcType); auto block_builder = mlir::OpBuilder::atBlockBegin(func.addEntryBlock() /* TODO: Add logger here */); GraphConverter graph_converter(context, &block_builder); @@ -269,9 +272,8 @@ NodePtr ngraph_to_mlir_op(MLIRContext* context, if(0 == input_map.count(symbol)) { input_map[symbol] = Index(i, j); } else { - OPENVINO_MLIR_DEBUG_PRINT( - "[ DEBUG ] Lost equality constraint for dimensions in output " << input << ".\n" << - " If the constraint is violated in runtime it will result in the undefined behaviour.\n"); + OPENVINO_MLIR_DEBUG_PRINT("Lost equality constraint for dimensions in output " << input << "."); + OPENVINO_MLIR_DEBUG_PRINT("If the constraint is violated in runtime it will result in the undefined behaviour."); } } } @@ -317,6 +319,144 @@ void replace_subgraph(SubgraphPtr subgraph, NodePtr node) { } } +// Marks matched subgraphs with a custom function name so the +// Partitioner groups them into a dedicated MLIR function. +// The patterns are specified with the env var: +// OV_MLIR_PATTERNS="name1=Type1,Type2;name2=Type3,Type4,...". +class PatternMatcher : public ov::pass::ModelPass { + struct NamedPattern { + std::string name; + std::vector types; + }; + + const std::vector patterns = []() { + std::vector patterns; + const auto& spec = ov::util::getenv_string("OV_MLIR_PATTERNS"); + size_t pos = 0; + while (pos < spec.size()) { + auto sep = spec.find(';', pos); + auto entry = spec.substr(pos, sep == std::string::npos ? std::string::npos : sep - pos); + pos = sep == std::string::npos ? spec.size() : sep + 1; + + auto eq = entry.find('='); + if (eq == std::string::npos) + continue; + NamedPattern p{entry.substr(0, eq), {}}; + for (size_t tp = eq + 1; tp < entry.size();) { + auto comma = entry.find(',', tp); + auto type = entry.substr(tp, comma == std::string::npos ? std::string::npos : comma - tp); + if (!type.empty()) + p.types.push_back(type); + tp = comma == std::string::npos ? entry.size() : comma + 1; + } + if (!p.name.empty() && !p.types.empty()) + patterns.push_back(std::move(p)); + } + return patterns; + }(); + +public: + OPENVINO_RTTI("PatternMatcher"); + + bool run_on_model(const std::shared_ptr& model) override { + if (patterns.empty()) + return false; + + auto ordered_ops = model->get_ordered_ops(); + auto filter = std::remove_if(ordered_ops.begin(), ordered_ops.end(), [](const auto& n) { + static std::string skip[] = {"Constant", "Parameter", "Result"}; + for (const auto& s : skip) { + if (s == n->get_type_info().name) + return true; + } + return false; + }); + ordered_ops.erase(filter, ordered_ops.end()); + + auto print_chain = [&](const char* msg, size_t offset, size_t count) { + std::string chain; + for (size_t i = offset; i < offset + count; ++i) { + chain += ordered_ops[i]->get_type_info().name; + chain += ","; + } + if (!chain.empty()) { + chain.pop_back(); + OPENVINO_MLIR_DEBUG_PRINT(msg << chain); + } + }; + + if (::ov::intel_gpu::mlir::is_debug()) + print_chain("Matching model: ", 0, ordered_ops.size()); + + bool changed = false; + std::unordered_set matched; + // Per-pattern match counter: each match gets a unique name (name, name1, name2, ...) so + // repeated matches stay separate single-output subgraphs instead of being force-joined by name. + std::unordered_map match_count; + for (size_t i = 0, count = ordered_ops.size(); i < count; ++i) { + auto node = ordered_ops[i]; + for (const auto& p : patterns) { + if (p.types.size() > count - i || p.types.front() != node->get_type_info().name || !has_subgraph_mark(node)) + continue; + bool all_matches = true; + size_t len = p.types.size(); + for (size_t n = i + 1, t = 1; t < len; ++n, ++t) { + if (p.types[t] != ordered_ops[n]->get_type_info().name || !has_subgraph_mark(ordered_ops[n])) { + all_matches = false; + break; + } + } + // Connectivity check + if (all_matches && len > 1) { + std::unordered_set nodes; + for (size_t t = 0; t < len; ++t) + nodes.insert(ordered_ops[i + t].get()); + std::unordered_set seen{ordered_ops[i].get()}; + std::vector stack{ordered_ops[i].get()}; + auto visit = [&](ov::Node* nb) { + if (nodes.count(nb) && seen.insert(nb).second) + stack.push_back(nb); + }; + while (!stack.empty()) { + auto* cur = stack.back(); + stack.pop_back(); + for (auto& in : cur->input_values()) + visit(in.get_node()); + for (auto& out : cur->outputs()) + for (auto& ti : out.get_target_inputs()) + visit(ti.get_node()); + } + all_matches = seen.size() == len; + } + if (all_matches) { + int n = match_count[p.name]++; + std::string name = n ? p.name + "#" + std::to_string(n) : p.name; + if (::ov::intel_gpu::mlir::is_debug()) { + std::string msg = "Matched pattern " + name + "="; + print_chain(msg.c_str(), i, len); + } + for (size_t t = 0; t < len; ++t, ++i) { + set_subgraph_mark(ordered_ops[i], name); + matched.insert(ordered_ops[i].get()); + } + changed = true; + --i; + break; + } + } + } + + // Clear marks on nodes not matched by any pattern - they will not be converted to MLIR. + for (auto node : ordered_ops) { + if (matched.count(node.get()) == 0) { + set_subgraph_mark(node, ""); + changed = true; + } + } + + return changed; + } +}; class Partitioner : public ov::pass::ModelPass { MLIRContext* context; @@ -331,19 +471,12 @@ class Partitioner : public ov::pass::ModelPass { bool run_on_model(const std::shared_ptr& model) override { SubgraphTracker tracker([this](SubgraphPtr subgraph) { - auto mlir_op = ngraph_to_mlir_op(context, subgraph, loweringContext); - replace_subgraph(subgraph, mlir_op); - OPENVINO_MLIR_DEBUG_PRINT("Created MLIR op: " << mlir_op << "\n"); - } - ); - for(auto node: model->get_ordered_ops()) { - if (auto name = get_subgraph_mark(node); !name.empty()) { - tracker.add_node(node, true); - if (auto subgraph = tracker.get_current_subgraph(node)) - subgraph->function_name = name; - } else { - tracker.add_node(node, false); - } + auto mlir_op = ngraph_to_mlir_op(context, subgraph, loweringContext); + replace_subgraph(subgraph, mlir_op); + OPENVINO_MLIR_DEBUG_PRINT("Created MLIR op: " << mlir_op); + }); + for (auto node : model->get_ordered_ops()) { + tracker.add_node(node, get_subgraph_mark(node)); } tracker.finalize(); return true; @@ -387,6 +520,7 @@ void injectMLIR(std::shared_ptr model, manager.register_pass(); manager.register_pass(); manager.register_pass(); + manager.register_pass(); manager.register_pass(context, loweringContext); manager.run_passes(model); model->validate_nodes_and_infer_types(); diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp index 357dc4ea54eb60..b6ea331630c9b4 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.cpp @@ -47,7 +47,10 @@ void GraphConverter::addOutputs(NodePtr node, mlir::Operation* op) { } void GraphConverter::convert(NodePtr node) { - auto convertor = node->get_rt_info()[rt_info_convertor()].as(); + const auto& rt_info = node->get_rt_info(); + auto it = rt_info.find(rt_info_convertor()); + OPENVINO_ASSERT(it != rt_info.end(), "No MLIR converter registered for node ", node->get_type_name()); + auto convertor = it->second.as(); auto mlirOp = convertor(_ctx, node); addOutputs(node, mlirOp); } diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp index 7c4e1a3c19a873..5e83be29bd48e4 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp @@ -14,6 +14,7 @@ #include "mlir/Dialect/Bufferization/Transforms/Passes.h" #include "mlir/Pass/PassManager.h" #include "openvino/runtime/intel_gpu/remote_properties.hpp" +#include "common/convert_common.hpp" #include "interface/properties.hpp" namespace ov::intel_gpu::mlir { @@ -41,6 +42,12 @@ static cl_device_id extract_device_from_context(cl_context context) { MLIREvaluateGcGPU::MLIREvaluateGcGPU(OwningOpRef<::mlir::ModuleOp> _module, std::shared_ptr loweringContext) { + if (::ov::intel_gpu::mlir::is_debug()) { + OPENVINO_MLIR_DEBUG_PRINT("-------------- Source MLIR --------------"); + _module->dump(); + OPENVINO_MLIR_DEBUG_PRINT("-----------------------------------------"); + } + gc::gpu::OclModuleBuilderOpts opts; gc::gpu::OclModuleBuilder builder(std::move(_module), opts); diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp index ebd6e169e166d2..87218f95d59834 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.cpp @@ -19,14 +19,21 @@ void Subgraph::merge (Subgraph& other) { SubgraphTracker::SubgraphTracker(Finalizer finalizer): m_finalizer(finalizer) {} -void SubgraphTracker::add_node (NodePtr node, bool belongs) { +void SubgraphTracker::add_node (NodePtr node, const std::string& name) { + const bool belongs = !name.empty(); // collect all subgraph ids that input nodes belong to and all dependencies Dependencies input_subgraphs; Dependencies input_dependencies; for(auto input_value: node->input_values()) { auto node = input_value.get_node_shared_ptr(); if(auto id = get_subgraph_id(node)) { - input_subgraphs.insert(ov::symbol::ancestor_of(id)); + id = ov::symbol::ancestor_of(id); + // Only merge with input subgraphs of the same name; a differently named input subgraph + // becomes a boundary (its output feeds this node's subgraph as an external input). + if(belongs && get_subgraph(id)->function_name == name) + input_subgraphs.insert(id); + else + input_dependencies.insert(id); } const auto& deps = get_dependencies(node); for(auto dep: deps) { @@ -50,6 +57,7 @@ void SubgraphTracker::add_node (NodePtr node, bool belongs) { // start a new subgraph auto subgraph_id = new_subgraph(); + get_subgraph(subgraph_id)->function_name = name; add_node_to_subgraph(node, subgraph_id); set_subgraph_id(node, subgraph_id); input_dependencies.insert(input_subgraphs.begin(), input_subgraphs.end()); diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp index 26b1f6819d0176..d5f44cf84a39c6 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/subgraph_tracker.hpp @@ -34,7 +34,9 @@ class SubgraphTracker { using Finalizer = std::function; SubgraphTracker(Finalizer finalizer); - void add_node (NodePtr node, bool belongs); + // name == "" means the node does not belong to any subgraph. Nodes that belong are merged only with + // connected input subgraphs sharing the same name; a different name starts a separate (named) subgraph. + void add_node (NodePtr node, const std::string& name); void finalize(); SubgraphPtr get_current_subgraph(NodePtr node); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp index 0cf1e4fe38a703..2b3ade618e3b43 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp @@ -17,15 +17,12 @@ #include "openvino/op/transpose.hpp" #include "shared_test_classes/base/benchmark.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" -#include "common_test_utils/ov_plugin_cache.hpp" -#include "openvino/runtime/intel_gpu/properties.hpp" +#include "openvino/util/env_util.hpp" namespace { static bool is_mlir_enabled() { - return ov::test::utils::PluginCache::get() - .core()->get_property(ov::test::utils::DEVICE_GPU, - ov::intel_gpu::enable_mlir); + return ov::util::getenv_bool("OV_GPU_ENABLE_MLIR"); } // A(1xSEQx1536xf16) diff --git a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp index b682a91c35a63e..36a64462be2d44 100644 --- a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp +++ b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp @@ -10,18 +10,14 @@ #include "openvino/op/reduce_sum.hpp" #include "openvino/op/add.hpp" #include "openvino/op/multiply.hpp" -#include "openvino/runtime/intel_gpu/properties.hpp" -#include "common_test_utils/ov_plugin_cache.hpp" -#include "openvino/runtime/intel_gpu/properties.hpp" +#include "openvino/util/env_util.hpp" namespace { using ov::test::InputShape; static bool is_mlir_enabled() { - return ov::test::utils::PluginCache::get() - .core()->get_property(ov::test::utils::DEVICE_GPU, - ov::intel_gpu::enable_mlir); + return ov::util::getenv_bool("OV_GPU_ENABLE_MLIR"); } using ReduceInputParams = std::tuple< ov::Shape, // Input shapes From 4235cd42a16a3d106a28a41a16651ecf0f2f25be Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Mon, 27 Jul 2026 15:57:32 +0200 Subject: [PATCH 095/121] Fix compile warnings (#18) * Fix compile warnings Signed-off-by: dchigarev * fix rtti warnings Signed-off-by: dchigarev * revert assert changes Signed-off-by: dchigarev --------- Signed-off-by: dchigarev --- .../mlir/common/convert_common.cpp | 6 +-- .../mlir/common/converters/binary_eltwise.hpp | 1 - .../mlir/common/converters/concat.hpp | 1 - .../mlir/common/converters/matmul.hpp | 2 +- .../mlir/common/converters/reduce.hpp | 2 +- .../mlir/common/converters/slice.hpp | 1 - .../mlir/common/converters/squeeze.hpp | 2 +- .../mlir/conversion/patterns.hpp | 28 ++++++------- .../plugin/transformations/mlir/convert.cpp | 40 ++++++++++++------- .../transformations/mlir/graph_converter.hpp | 2 +- 10 files changed, 47 insertions(+), 38 deletions(-) diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp index 2f9ab1ccf48f05..2c6e10547a8c33 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp @@ -86,7 +86,7 @@ BroadcastDimensions broadcast_dimensions(const PartialShape& src, const PartialS ReassociationIndices group; bool group_bonded = false; // true if `group` has a non-brodcasted dimension - size_t dst_i = 0; // dimension index in the `dst` shape + int64_t dst_i = 0; // dimension index in the `dst` shape for(; dst_i < offset; ++dst_i) { dimensions.push_back(dst_i); } @@ -164,7 +164,7 @@ bool has_dynamic_rank(NodePtr node) { bool are_equal_dimensions(Dimension d1, Dimension d2) { return - d1.is_static() && d2.is_static() && d1 == d2 + (d1.is_static() && d2.is_static() && d1 == d2) || ov::symbol::are_equal(d1.get_symbol(), d2.get_symbol()); } @@ -186,7 +186,7 @@ bool statically_broadcastable(const PartialShape& from, const PartialShape& to) } auto offset = to_rank - from_rank; - for(size_t i = 0; i < from_rank; ++i) { + for(int64_t i = 0; i < from_rank; ++i) { auto d_from = from[i]; auto d_to = to[offset + i]; if(!are_equal_dimensions(d_from, d_to) && !has_broadcast(d_from, d_to)) { diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp index f210ddff82701e..3cd58df846b530 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/binary_eltwise.hpp @@ -28,7 +28,6 @@ struct ConvertBinaryEltwise { const auto ov_output_element_type = node->get_output_element_type(0); const auto ov_output_shape = node->get_output_partial_shape(0); auto outType = importTensor(context.context, ov_output_shape, ov_output_element_type); - const int output_rank = ov_output_shape.rank().get_length(); SmallVector dynamic_dimensions = context.get_dynamic_dimension_values(ov_output_shape); diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp index a4b8574d999396..ddbe25f719f2fa 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/concat.hpp @@ -21,7 +21,6 @@ struct ConvertConcat { auto& builder = context.builder(); const auto inputs = context.getInputs(node); - const auto ov_element_type = node->get_input_element_type(0); const auto src_partial_shape = node->get_input_partial_shape(0); const auto rank = src_partial_shape.rank().get_length(); diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp index 431116d9a78159..8802a648b98520 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/matmul.hpp @@ -70,7 +70,7 @@ struct ConvertMatMul { int64_t rank = ov_output_shape.size(); auto type = mlir::cast(tensor.getType()); auto shape = type.getShape(); - if (shape.size() == rank) + if (static_cast(shape.size()) == rank) return tensor; SmallVector reassoc; ReassociationIndices leading; diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp index ca4b404cb4bc77..4eec227e357a7b 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp @@ -48,7 +48,7 @@ struct ConvertReduce { ::mlir::RankedTensorType result_type; { SmallVector shape; - for (size_t i = 0; i < input_rank; ++i) { + for (int64_t i = 0; i < input_rank; ++i) { if (!llvm::is_contained(reduction_axes, i)) { auto dim = input_shape[i]; assert(dim.is_static() && "Dynamic shapes not supported"); diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp index f618e61f9a8e5c..a34442712edcc1 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/slice.hpp @@ -22,7 +22,6 @@ struct ConvertSlice { const auto start = context.getInputs(node)[1]; const auto stop = context.getInputs(node)[2]; const auto step = context.getInputs(node)[3]; - const auto axes = context.getInputs(node)[4]; const auto ov_index_shape = node->get_input_partial_shape(1); const auto ov_index_element_type = node->get_input_element_type(1); diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp index af6a2a40b80cc8..bedc3d6dd498cf 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/squeeze.hpp @@ -25,7 +25,7 @@ struct ConvertSqueeze { auto src_rank = src_partial_shape.rank().get_length(); SmallVector collapse_groups; ReassociationIndices group = ReassociationIndices(); - for (size_t src_i = 0; src_i < src_rank; src_i++) { + for (int64_t src_i = 0; src_i < src_rank; src_i++) { auto src_d = src_partial_shape[src_i]; group.push_back(src_i); if (src_d.is_static() && src_d.get_length() == 1) { diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp index ccb62d2f347517..a4a9b54c3f1407 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp @@ -14,86 +14,86 @@ namespace ov::intel_gpu::mlir { class ReluPattern : public MarkPattern { public: - OPENVINO_RTTI("ReluPattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("ReluPattern"); ReluPattern(); }; class ConcatPattern : public MarkPattern { public: - OPENVINO_RTTI("ConcatPattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("ConcatPattern"); ConcatPattern(); }; class FloorPattern : public MarkPattern { public: - OPENVINO_RTTI("FloorPattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("FloorPattern"); FloorPattern(); }; class GatherPattern : public MarkPattern { public: - OPENVINO_RTTI("GatherPattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("GatherPattern"); GatherPattern(); }; class MatMulPattern : public MarkPattern { public: - OPENVINO_RTTI("MatMulPattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("MatMulPattern"); MatMulPattern(); }; template class ReducePattern : public MarkPattern { public: - OPENVINO_RTTI("ReducePattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("ReducePattern"); ReducePattern(); }; class ReshapePattern : public MarkPattern { public: - OPENVINO_RTTI("ReshapePattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("ReshapePattern"); ReshapePattern(); }; class SDPAPattern : public MarkPattern { public: - OPENVINO_RTTI("SDPAPattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("SDPAPattern"); SDPAPattern(); }; class ShapeOfPattern : public MarkPattern { public: - OPENVINO_RTTI("ShapeOfPattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("ShapeOfPattern"); ShapeOfPattern(); }; class SlicePattern : public MarkPattern { public: - OPENVINO_RTTI("SlicePattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("SlicePattern"); SlicePattern(); }; class SqueezePattern : public MarkPattern { public: - OPENVINO_RTTI("SqueezePattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("SqueezePattern"); SqueezePattern(); }; class TransposePattern : public MarkPattern { public: - OPENVINO_RTTI("TransposePattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("TransposePattern"); TransposePattern(); }; class UnsqueezePattern : public MarkPattern { public: - OPENVINO_RTTI("UnsqueezePattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("UnsqueezePattern"); UnsqueezePattern(); }; class BinaryEltwisePatternBase : public MarkPattern { public: - OPENVINO_RTTI("BinaryEltwisePatternBase", "0"); + OPENVINO_MATCHER_PASS_RTTI("BinaryEltwisePatternBase"); BinaryEltwisePatternBase(NodeTypeInfo wrapped_type, GraphConverter::Convertor convertor, const std::set& element_types = {}); }; diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp index 04fee0d7a417d3..28055751a3219f 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp @@ -137,7 +137,8 @@ void dropUnusedInputArgs(mlir::func::FuncOp func, size_t numInputs, SmallVector< } kept.push_back(i); } - func.eraseArguments(toErase); + [[maybe_unused]] auto result = func.eraseArguments(toErase); + assert(mlir::succeeded(result) && "Failed to erase unused function arguments"); } mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, @@ -187,22 +188,22 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, // transition from memref enclosure to tensor interior auto ranked = mlir::dyn_cast(funcInputVal.getType()); auto tensorTy = mlir::RankedTensorType::get(ranked.getShape(), ranked.getElementType()); - auto tensor = block_builder.create( - loc, tensorTy, funcInputVal, /*restrict = */ true, /*writable=*/ true); + auto tensor = bufferization::ToTensorOp::create( + block_builder, loc, tensorTy, funcInputVal, /*restrict = */ true, /*writable=*/ true); graph_converter.nodeOutputMap.emplace(inputs[i], tensor); // FIXME: Avoid pre-population of dimension_map, take dimension values only if needed auto input_shape = inputs[i].get_partial_shape(); auto input_rank = input_shape.rank(); if(input_rank.is_static()) { - for(size_t j = 0; j < input_rank.get_length(); ++j) { + for(int64_t j = 0; j < input_rank.get_length(); ++j) { auto dim = input_shape[j]; if(dim.is_dynamic()) { auto symbol = dim.get_symbol(); assert(symbol); symbol = ov::symbol::ancestor_of(symbol); if(dim.is_dynamic() && !graph_converter.dimension_map.count(symbol)) { - auto dimSize = block_builder.create(loc, tensor, j); + auto dimSize = tensor::DimOp::create(block_builder, loc, tensor, j); graph_converter.dimension_map[symbol] = dimSize; } } @@ -226,16 +227,17 @@ mlir::OwningOpRef ngraph_to_mlir(MLIRContext* context, // Ensure the result is stored in the provided function argument. // Mark as restrict to avoid temporary buffer and copy. // Mark as writable to ensure the output can be written to the buffer. - block_builder.create(loc, - TypeRange{}, - tensor, - memref, - /*restrict=*/true, - /*writable=*/true); + bufferization::MaterializeInDestinationOp::create(block_builder, + loc, + TypeRange{}, + tensor, + memref, + /*restrict=*/true, + /*writable=*/true); } const auto retLoc = createLayerLocation(context, "output", "Output"); - block_builder.create(retLoc, ArrayRef(SmallVector())); + mlir::func::ReturnOp::create(block_builder, retLoc, ArrayRef(SmallVector())); SmallVector keptRuntimeIndices; dropUnusedInputArgs(func, runtime_inputs.size(), keptRuntimeIndices); auto runtime_to_original = llvm::map_to_vector( @@ -319,6 +321,10 @@ void replace_subgraph(SubgraphPtr subgraph, NodePtr node) { } } +} // namespace + +namespace ov::intel_gpu::mlir { + // Marks matched subgraphs with a custom function name so the // Partitioner groups them into a dedicated MLIR function. // The patterns are specified with the env var: @@ -356,7 +362,7 @@ class PatternMatcher : public ov::pass::ModelPass { }(); public: - OPENVINO_RTTI("PatternMatcher"); + OPENVINO_MODEL_PASS_RTTI("PatternMatcher"); bool run_on_model(const std::shared_ptr& model) override { if (patterns.empty()) @@ -462,7 +468,7 @@ class Partitioner : public ov::pass::ModelPass { MLIRContext* context; std::shared_ptr loweringContext; public: - OPENVINO_RTTI("Partitioner"); + OPENVINO_MODEL_PASS_RTTI("Partitioner"); Partitioner(MLIRContext* context, std::shared_ptr loweringContext) : context(context), @@ -483,6 +489,12 @@ class Partitioner : public ov::pass::ModelPass { } }; +} // namespace ov::intel_gpu::mlir + +namespace { + +using namespace mlir; +using namespace ov::intel_gpu::mlir; void injectMLIR(std::shared_ptr model, MLIRContext* context, diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp index 179e1b3c2ecfa7..e85276c0fb1256 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/graph_converter.hpp @@ -60,7 +60,7 @@ std::string get_subgraph_mark(NodePtr node); class MarkPattern : public ov::pass::MatcherPass { public: - OPENVINO_RTTI("MarkPattern", "0"); + OPENVINO_MATCHER_PASS_RTTI("MarkPattern"); MarkPattern(NodePtr pattern, GraphConverter::Convertor convertor); using Callback = std::function; MarkPattern(NodePtr pattern, Callback callback); From a87821c537b3ffa1be7df0d36758a1f573907490 Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Mon, 27 Jul 2026 17:47:33 +0200 Subject: [PATCH 096/121] Align SDPA-mlir matcher with converter restrictions (#19) * Do not match broken sdpa cases Signed-off-by: dchigarev * Fix matching Signed-off-by: dchigarev --------- Signed-off-by: dchigarev --- .../mlir/conversion/patterns.cpp | 53 ++++++++++++++++++- .../tests/functional/mlir_op/sdpa.cpp | 30 +++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp index eb93eba792a441..114c3c35d98b63 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -92,7 +93,57 @@ ReshapePattern::ReshapePattern() : MarkPattern(wrap_type({any_input(), any_input()}), ConvertReshape()) {} SDPAPattern::SDPAPattern() - : MarkPattern(wrap_type(), ConvertSDPA()) {} + : MarkPattern( + wrap_type([](const Output& output) { + auto node = std::dynamic_pointer_cast(output.get_node_shared_ptr()); + if (!node) { + return false; + } + + // Query, Key, Value ranks must be static, equal, and either 3D or 4D + const auto q_shape = node->get_input_partial_shape(0); + const auto k_shape = node->get_input_partial_shape(1); + const auto v_shape = node->get_input_partial_shape(2); + if (q_shape.rank().is_dynamic() || k_shape.rank().is_dynamic() || v_shape.rank().is_dynamic()) { + return false; + } + const auto q_rank = q_shape.rank().get_length(); + if (q_rank != k_shape.rank().get_length() || q_rank != v_shape.rank().get_length()) { + return false; + } + if (q_rank != 3 && q_rank != 4) { + return false; + } + + // Causal attention is not supported + if (node->get_causal()) { + return false; + } + + const auto input_size = node->get_input_size(); + // Sink parameter (6th input) is not supported + if (input_size >= 6) { + return false; + } + + // Mask (input 3): only static shapes are supported, dynamic ones are rejected + if (input_size > 3) { + const auto mask_shape = node->get_input_partial_shape(3); + const bool has_mask = mask_shape.rank().is_dynamic() || mask_shape.rank().get_length() > 0; + if (has_mask && mask_shape.is_dynamic()) { + return false; + } + } + + // Scale (input 4) must be a Constant, dynamic scale input is not supported + if (input_size > 4 && + !std::dynamic_pointer_cast(node->get_input_node_shared_ptr(4))) { + return false; + } + + return true; + }), + ConvertSDPA()) {} ShapeOfPattern::ShapeOfPattern() : MarkPattern(wrap_type({any_input()}), ConvertShapeOf()) {} diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp index c2544bd36363da..069db1466e32fb 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp @@ -16,6 +16,7 @@ #include "intel_gpu/runtime/execution_config.hpp" #include "openvino/op/transpose.hpp" +#include "openvino/runtime/exec_model_info.hpp" namespace { using ov::test::InputShape; @@ -40,6 +41,7 @@ class ScaledAttnLayerGPUMlirTest : public testing::WithParamInterface& targetInputStaticShapes) override; void transpose_prepare(std::vector& shapes, const std::vector>& input_transpose); + void check_mlir_execution(); bool is_causal; bool has_attn; bool is_attn_const; @@ -320,8 +322,36 @@ void ScaledAttnLayerGPUMlirTest::generate_inputs(const std::vector& t } } +void ScaledAttnLayerGPUMlirTest::check_mlir_execution() { + auto exec_model = compiledModel.get_runtime_model(); + ASSERT_NE(exec_model, nullptr); + + bool has_mlir_op = false; + bool has_sdpa = false; + for (const auto& node : exec_model->get_ordered_ops()) { + const auto& rt_info = node->get_rt_info(); + auto it = rt_info.find(ov::exec_model_info::LAYER_TYPE); + if (it == rt_info.end()) { + continue; + } + const auto layer_type = it->second.as(); + if (layer_type == "mlir_primitive" || layer_type == "MLIROp") { + has_mlir_op = true; + } else if (layer_type == "ScaledDotProductAttention" || layer_type == "scaled_dot_product_attention") { + has_sdpa = true; + } + } + + // The SDPA pattern must have matched: execution goes through MLIROp and no + // ScaledDotProductAttention primitive is left in the execution graph. + EXPECT_TRUE(has_mlir_op) << "Expected an MLIROp in the execution graph, but none was found. " + << "Is 'OV_GPU_ENABLE_MLIR=1' ?"; + EXPECT_FALSE(has_sdpa) << "Unexpected ScaledDotProductAttention in the execution graph"; +} + TEST_P(ScaledAttnLayerGPUMlirTest, CompareWithRefs) { run(); + check_mlir_execution(); } const std::vector> disable_transpose{}; From 151ac182703e128f0f0b3b85e57803f0cd8e0838 Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Mon, 27 Jul 2026 17:56:15 +0200 Subject: [PATCH 097/121] Remove 'revert-pvc-support' patch from CI (#21) Signed-off-by: Dmitry Chigarev --- .github/workflows/graph-compiler.yml | 101 --------------------------- 1 file changed, 101 deletions(-) diff --git a/.github/workflows/graph-compiler.yml b/.github/workflows/graph-compiler.yml index 7ef96c30e62484..626d3747320732 100644 --- a/.github/workflows/graph-compiler.yml +++ b/.github/workflows/graph-compiler.yml @@ -22,107 +22,6 @@ jobs: with: submodules: recursive - - name: Apply GPU revert patch - run: | - cat > /tmp/revert.patch <<'PATCH_EOF' - diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp - --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp - +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/common_utils.hpp - @@ -44,9 +44,10 @@ - const uint64_t LARGE_OUTPUT_BYTES_THRESHOLD = 4 * 1048576; - - const auto& device_info = engine.get_device_info(); - - if ((device_info.gfx_ver.major >= 20 && device_info.dev_type == cldnn::device_type::discrete_gpu) || - + if ((device_info.gfx_ver.major == 12 && device_info.gfx_ver.minor == 60) || - + (device_info.gfx_ver.major >= 20 && device_info.dev_type == cldnn::device_type::discrete_gpu) || - (device_info.dev_type == cldnn::device_type::discrete_gpu && total_output_bytes > LARGE_OUTPUT_BYTES_THRESHOLD)) { - - // WA: Disable USM host memory for infer request`s tensors for dGPUs, as kernel access - + // WA: Disable USM host memory for infer request`s tensors for PVC and subsequent dGPUs, as kernel access - // to system memory is slower than using an explicit memcpy (Host <-> Device) call with the copy engine - // Driver tickets with additional details: 6155, 10054 - GPU_DEBUG_TRACE << "Do not use usm_host for performance issue" << std::endl; - diff --git a/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp b/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp - --- a/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp - +++ b/src/plugins/intel_gpu/src/graph/impls/ocl_v2/sdpa/sdpa_gen_micro.cpp - @@ -1533,7 +1533,10 @@ - case gpu_arch::xe_hpg: { - config = choose_config_xehpg(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_paged_attention, is_prefill); - break; - } - + case gpu_arch::xe_hpc: - + config = choose_config_xehpc(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_integrated, is_paged_attention, is_prefill); - + break; - case gpu_arch::xe2: - case gpu_arch::xe3: - config = choose_config_xe2(static_cast(k_head_size), nkeys_v, thin_q, is_quantized, is_integrated, is_paged_attention, is_prefill); - diff --git a/src/plugins/intel_gpu/src/runtime/device.cpp b/src/plugins/intel_gpu/src/runtime/device.cpp - --- a/src/plugins/intel_gpu/src/runtime/device.cpp - +++ b/src/plugins/intel_gpu/src/runtime/device.cpp - @@ -101,6 +101,8 @@ - { { {12, 0, 0}, {12, 9, MAX_REVISION} }, { 0, 16, 32, 0, 0, 64 }, {} }, // TGL, RKL, ADL - { { {12, 10, 0} }, { 0, 16, 32, 0, 0, 64 }, {} }, // DG1 - { { {12, 55, 0}, {12, 57, MAX_REVISION} }, { 0, 16, 32, 128, 256, 0 }, {} }, // DG2 - + { { {12, 60, 0}, {12, 60, 1} }, { 16, 32, 64, 512, 1024, 0 }, {} }, // PVC_XL - + { { {12, 60, 3}, {12, 61, 7} }, { 32, 32, 64, 512, 1024, 0 }, {} }, // PVC_XT - { { {12, 70, 0}, {12, 71, MAX_REVISION} }, { 0.5, 16, 32, 0, 0, 64 }, {} }, // MTL/ARL-S - { { {12, 74, 0}, {12, 74, MAX_REVISION} }, { 0.5, 16, 32, 128, 256, 0 }, {} }, // ARL-H - { { {20, 1, 0}, {20, 2, MAX_REVISION} }, { 1, 16, 32, 128, 256, 0 }, {} }, // BMG - diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp - --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp - +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_device.cpp - @@ -60,7 +60,7 @@ - case ngen::HW::XeLP: return gpu_arch::xe_lp; - case ngen::HW::XeHP: return gpu_arch::xe_hp; - case ngen::HW::XeHPG: return gpu_arch::xe_hpg; - - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); - + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; - case ngen::HW::Xe2: return gpu_arch::xe2; - case ngen::HW::Xe3: return gpu_arch::xe3; - case ngen::HW::Xe3p: return gpu_arch::xe3p; - diff --git a/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp b/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp - --- a/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp - +++ b/src/plugins/intel_gpu/src/runtime/sycl/sycl_device.cpp - @@ -64,7 +64,7 @@ - case ngen::HW::XeLP: return gpu_arch::xe_lp; - case ngen::HW::XeHP: return gpu_arch::xe_hp; - case ngen::HW::XeHPG: return gpu_arch::xe_hpg; - - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); - + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; - case ngen::HW::Xe2: return gpu_arch::xe2; - case ngen::HW::Xe3: return gpu_arch::xe3; - case ngen::HW::Xe3p: return gpu_arch::xe3p; - diff --git a/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp b/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp - --- a/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp - +++ b/src/plugins/intel_gpu/src/runtime/ze/ze_device.cpp - @@ -45,7 +45,7 @@ - case ngen::HW::XeLP: return gpu_arch::xe_lp; - case ngen::HW::XeHP: return gpu_arch::xe_hp; - case ngen::HW::XeHPG: return gpu_arch::xe_hpg; - - case ngen::HW::XeHPC: OPENVINO_THROW("[GPU] XeHPC is not supported"); - + case ngen::HW::XeHPC: return gpu_arch::xe_hpc; - case ngen::HW::Xe2: return gpu_arch::xe2; - case ngen::HW::Xe3: return gpu_arch::xe3; - case ngen::HW::Xe3p: return gpu_arch::xe3p; - diff --git a/src/plugins/intel_gpu/thirdparty/CMakeLists.txt b/src/plugins/intel_gpu/thirdparty/CMakeLists.txt - --- a/src/plugins/intel_gpu/thirdparty/CMakeLists.txt - +++ b/src/plugins/intel_gpu/thirdparty/CMakeLists.txt - @@ -14,6 +14,6 @@ - set(ONEDNN_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_build") - set(ONEDNN_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_install" CACHE PATH "Installation path for oneDNN GPU library") - set(ONEDNN_PREFIX_DIR "${CMAKE_CURRENT_BINARY_DIR}/onednn_gpu_root") - - set(ONEDNN_ENABLED_PRIMITIVES "CONCAT;CONVOLUTION;DECONVOLUTION;GATED_MLP;MATMUL;REORDER;POOLING;REDUCTION;RNN") - - set(ONEDNN_ENABLED_ISA "XELP;XEHP;XEHPG;XE2;XE3;XE3P") - + set(ONEDNN_ENABLED_PRIMITIVES "CONCAT;CONVOLUTION;DECONVOLUTION;GATED_MLP;INNER_PRODUCT;MATMUL;REORDER;POOLING;REDUCTION;SDPA;RNN") - + set(ONEDNN_ENABLED_ISA "ALL") - set(DNNL_GPU_LIBRARY_NAME "openvino_onednn_gpu" CACHE STRING "Name of oneDNN library for Intel GPU Plugin") - PATCH_EOF - # Idempotent: skip if the patch is already applied (e.g. reused workspace). - if git -C "$GITHUB_WORKSPACE" apply --reverse --check /tmp/revert.patch 2>/dev/null; then - echo "Revert patch already applied, skipping." - else - git -C "$GITHUB_WORKSPACE" apply -v /tmp/revert.patch - fi - - name: Build run: | mkdir -p $BUILD_DIR From 091b97b5aea80fc940f0318f83fc481ba445d998 Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Tue, 28 Jul 2026 13:15:11 +0200 Subject: [PATCH 098/121] Isolate MLIR tests (#22) * Exclude mlir-tests for non-gc build Signed-off-by: dchigarev * Revert reduce-test changes Signed-off-by: dchigarev --------- Signed-off-by: dchigarev --- src/plugins/intel_gpu/tests/functional/CMakeLists.txt | 9 ++++++++- .../tests/functional/single_layer_tests/reduce.cpp | 10 ++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/CMakeLists.txt b/src/plugins/intel_gpu/tests/functional/CMakeLists.txt index af939dcbff3860..4e8e8cd6898dea 100644 --- a/src/plugins/intel_gpu/tests/functional/CMakeLists.txt +++ b/src/plugins/intel_gpu/tests/functional/CMakeLists.txt @@ -13,6 +13,12 @@ endif() list(APPEND DEFINES TEST_CUSTOM_OP_CONFIG_PATH="${CMAKE_CURRENT_SOURCE_DIR}/custom_op/custom_op.xml") +# Exclude MLIR tests if graph compiler is disabled +set(EXCLUDED_TEST_PATHS) +if(NOT ENABLE_GRAPH_COMPILER) + list(APPEND EXCLUDED_TEST_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/mlir_op") +endif() + ov_add_test_target( NAME ${TARGET_NAME} @@ -20,6 +26,8 @@ ov_add_test_target( ${CMAKE_CURRENT_SOURCE_DIR} ADDITIONAL_SOURCE_DIRS ${TEST_COMMON_SOURCE_DIR} + EXCLUDED_SOURCE_PATHS + ${EXCLUDED_TEST_PATHS} INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} $/include/ @@ -37,7 +45,6 @@ ov_add_test_target( OV GPU ) - ov_gpu_set_runtime_interface_for(${TARGET_NAME}) if(ENABLE_PROXY) diff --git a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp index 36a64462be2d44..cd011d36c9537a 100644 --- a/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp +++ b/src/plugins/intel_gpu/tests/functional/single_layer_tests/reduce.cpp @@ -10,15 +10,11 @@ #include "openvino/op/reduce_sum.hpp" #include "openvino/op/add.hpp" #include "openvino/op/multiply.hpp" -#include "openvino/util/env_util.hpp" +#include "openvino/runtime/intel_gpu/properties.hpp" namespace { using ov::test::InputShape; - -static bool is_mlir_enabled() { - return ov::util::getenv_bool("OV_GPU_ENABLE_MLIR"); -} using ReduceInputParams = std::tuple< ov::Shape, // Input shapes ov::element::Type, // Input precision @@ -71,8 +67,6 @@ class ReduceSumSqueezeTest : public testing::WithParamInterface(add_node, mul_val_node); auto result = std::make_shared(mul_node); function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input_node}, "input"); - if (is_mlir_enabled()) - abs_threshold = 0.01f; } void run() override { @@ -86,7 +80,7 @@ class ReduceSumSqueezeTest : public testing::WithParamInterface Date: Fri, 31 Jul 2026 13:17:44 +0200 Subject: [PATCH 099/121] Test ov-ci (#25) Signed-off-by: dchigarev --- .github/workflows/dev_gpu_linux_mlir.yml | 458 +++++++++++++++++++++++ 1 file changed, 458 insertions(+) create mode 100644 .github/workflows/dev_gpu_linux_mlir.yml diff --git a/.github/workflows/dev_gpu_linux_mlir.yml b/.github/workflows/dev_gpu_linux_mlir.yml new file mode 100644 index 00000000000000..9766bdb15f02a6 --- /dev/null +++ b/.github/workflows/dev_gpu_linux_mlir.yml @@ -0,0 +1,458 @@ +name: Linux GPU MLIR / Graph Compiler ( Ubuntu 24.04 ) + +# Validates the MLIR / Graph-Compiler paths of the GPU plugin: +# * one build with -DENABLE_GRAPH_COMPILER=ON (LLVM + Graph Compiler are built from source) +# * all GPU unit tests with MLIR disabled (OV_GPU_ENABLE_MLIR=0) - regression guard: a +# GC-enabled build must behave exactly like a stock GPU build when the feature is off +# * all MLIR functional tests with MLIR enabled (OV_GPU_ENABLE_MLIR=1) +# +# `paths:` below narrows the trigger down to MLIR-related files. Smart CI is used on top of +# it for the usual reasons (docs-only skip, Docker image reuse), but it cannot replace +# `paths:` here: it is component-granular and would report the whole `GPU` component as +# affected for any GPU change. + +on: + workflow_dispatch: + inputs: + target-branch: + description: 'Target branch for the build; taken from event context by default' + type: string + required: false + graph-compiler-ref: + description: 'Graph Compiler branch/tag/SHA to build against' + type: string + required: false + pull_request: + types: + - opened + - synchronize + - reopened + - ready_for_review + paths: + - '.github/workflows/dev_gpu_linux_mlir.yml' + - 'cmake/graph-compiler.cmake' + - 'cmake/llvm.cmake' + - 'src/plugins/intel_gpu/CMakeLists.txt' + - 'src/plugins/intel_gpu/include/intel_gpu/op/mlir_op.hpp' + - 'src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp' + - 'src/plugins/intel_gpu/include/intel_gpu/runtime/options.inl' + - 'src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.*' + - 'src/plugins/intel_gpu/src/graph/include/mlir_primitive_inst.h' + - 'src/plugins/intel_gpu/src/graph/mlir_primitive.cpp' + - 'src/plugins/intel_gpu/src/graph/registry/mlir_primitive_impls.cpp' + - 'src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp' + - 'src/plugins/intel_gpu/src/plugin/transformations/mlir/**' + - 'src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp' + - 'src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp' + - 'src/plugins/intel_gpu/tests/functional/CMakeLists.txt' + - 'src/plugins/intel_gpu/tests/functional/mlir_op/**' + push: + branches: + - master + - 'releases/**' + paths: + - '.github/workflows/dev_gpu_linux_mlir.yml' + - 'cmake/graph-compiler.cmake' + - 'cmake/llvm.cmake' + - 'src/plugins/intel_gpu/CMakeLists.txt' + - 'src/plugins/intel_gpu/include/intel_gpu/op/mlir_op.hpp' + - 'src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp' + - 'src/plugins/intel_gpu/include/intel_gpu/runtime/options.inl' + - 'src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.*' + - 'src/plugins/intel_gpu/src/graph/include/mlir_primitive_inst.h' + - 'src/plugins/intel_gpu/src/graph/mlir_primitive.cpp' + - 'src/plugins/intel_gpu/src/graph/registry/mlir_primitive_impls.cpp' + - 'src/plugins/intel_gpu/src/plugin/ops/mlir_op.cpp' + - 'src/plugins/intel_gpu/src/plugin/transformations/mlir/**' + - 'src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp' + - 'src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp' + - 'src/plugins/intel_gpu/tests/functional/CMakeLists.txt' + - 'src/plugins/intel_gpu/tests/functional/mlir_op/**' + +concurrency: + # github.ref is not unique in post-commit + group: ${{ github.event_name == 'push' && github.run_id || github.ref }}-linux-gpu-mlir-dev + cancel-in-progress: true + +permissions: read-all + +env: + GRAPH_COMPILER_URL: 'https://github.com/dchigarev/graph-compiler.git' + GRAPH_COMPILER_REF: ${{ inputs.graph-compiler-ref || 'dchigarev/mlir-exp' }} + +jobs: + Smart_CI: + runs-on: ubuntu-latest + if: github.event.pull_request.draft == false || github.run_attempt > 1 + outputs: + affected_components: "${{ steps.smart_ci.outputs.affected_components }}" + changed_components: "${{ steps.smart_ci.outputs.changed_components }}" + skip_workflow: "${{ steps.smart_ci.outputs.skip_workflow }}" + steps: + - name: checkout action + uses: ababushk/checkout@dd591a6a2ac25618db4eda86e7e0d938f88cf01b # cherry_pick_retries + timeout-minutes: 15 + with: + sparse-checkout: .github/actions/smart-ci + + - name: Get affected components + id: smart_ci + uses: ./.github/actions/smart-ci + with: + repository: ${{ github.repository }} + pr: ${{ github.event.number }} + commit_sha: ${{ github.sha }} + ref_name: ${{ github.ref_name }} + component_pattern: "category: (.*)" + repo_token: ${{ secrets.GITHUB_TOKEN }} + skip_when_only_listed_labels_set: 'docs' + skip_when_only_listed_files_changed: '*.md,*.rst,*.png,*.jpg,*.svg,*/layer_tests_summary/*,*/conformance/*,.github/workflows/ci-doctor.lock.yml,.github/workflows/ci-doctor-mq.lock.yml' + + - name: Show affected components + run: | + echo "${{ toJSON(steps.smart_ci.outputs.affected_components) }}" + shell: bash + + Docker: + needs: Smart_CI + runs-on: aks-linux-4-cores-16gb-docker-build + container: + image: openvinogithubactions.azurecr.io/docker_build:0.2 + volumes: + - /mount:/mount + outputs: + images: "${{ steps.handle_docker.outputs.images && steps.handle_docker.outputs.images || steps.mock_image.outputs.images }}" + steps: + - name: Set mock output images if pipeline should be skipped + if: ${{ needs.smart_ci.outputs.skip_workflow == 'True' }} + id: mock_image + run: echo "images={\"ov_test\":{\"ubuntu_24_04_x64_dgpu\":\"mock\"},\"ov_build\":{\"ubuntu_24_04_x64\":\"mock\"}}" >> "$GITHUB_OUTPUT" + + - name: Checkout + if: ${{ needs.smart_ci.outputs.skip_workflow != 'True' }} + uses: ababushk/checkout@dd591a6a2ac25618db4eda86e7e0d938f88cf01b # cherry_pick_retries + timeout-minutes: 15 + + - uses: ./.github/actions/handle_docker + if: ${{ needs.smart_ci.outputs.skip_workflow != 'True' }} + id: handle_docker + with: + images: | + ov_build/ubuntu_24_04_x64 + ov_test/ubuntu_24_04_x64_dgpu + registry: 'openvinogithubactions.azurecr.io' + dockerfiles_root_dir: '.github/dockerfiles' + changed_components: ${{ needs.smart_ci.outputs.changed_components }} + + Build: + name: Build (LLVM + Graph Compiler + OpenVINO) + needs: [ Docker, Smart_CI ] + if: "!needs.smart_ci.outputs.skip_workflow && fromJSON(needs.smart_ci.outputs.affected_components).GPU" + timeout-minutes: 60 + defaults: + run: + shell: bash + runs-on: aks-linux-16-cores-64gb + container: + image: ${{ fromJSON(needs.docker.outputs.images).ov_build.ubuntu_24_04_x64 }} + volumes: + - /mount:/mount + - /home/runner/secrets/:/secrets:ro + - ${{ github.workspace }}:${{ github.workspace }} # Needed as ${{ github.workspace }} is not working correctly when using Docker + options: >- + -e SCCACHE_AZURE_BLOB_CONTAINER + env: + DEBIAN_FRONTEND: noninteractive # to prevent apt-get from waiting user input + CMAKE_BUILD_TYPE: 'Release' + CMAKE_GENERATOR: 'Ninja' + CMAKE_CXX_COMPILER_LAUNCHER: sccache + CMAKE_C_COMPILER_LAUNCHER: sccache + SCCACHE_IGNORE_SERVER_IO_ERROR: 1 + SCCACHE_SERVER_PORT: 35555 + SCCACHE_ERROR_LOG: ${{ github.workspace }}/sccache_log.txt + SCCACHE_LOG: warn + SCCACHE_AZURE_KEY_PREFIX: ubuntu_24_04_x86_64_mlir_Release + OPENVINO_REPO: ${{ github.workspace }}/openvino + BUILD_DIR: ${{ github.workspace }}/openvino_build + INSTALL_DIR: ${{ github.workspace }}/openvino_install + INSTALL_TEST_DIR: ${{ github.workspace }}/tests_install + GC_REPO: ${{ github.workspace }}/graph-compiler + # `scripts/compile.sh` installs LLVM into `/externals/llvm` and Graph Compiler + # into `/build/install` when GITHUB_ACTIONS=true. + LLVM_INSTALL_DIR: ${{ github.workspace }}/graph-compiler/externals/llvm + GC_INSTALL_DIR: ${{ github.workspace }}/graph-compiler/build/install + # LLVM_CACHE_DIR: /mount/caches/graph-compiler/llvm/ubuntu_24_04_x86_64_Release + + steps: + - name: Load SCCACHE_AZURE_CONNECTION_STRING from file + run: | + SCCACHE_AZURE_CONNECTION_STRING="$(cat /secrets/sccache/connection-string)" + echo "::add-mask::${SCCACHE_AZURE_CONNECTION_STRING}" + echo "SCCACHE_AZURE_CONNECTION_STRING=${SCCACHE_AZURE_CONNECTION_STRING}" >> "$GITHUB_ENV" + + - name: Clone OpenVINO + uses: ababushk/checkout@dd591a6a2ac25618db4eda86e7e0d938f88cf01b # cherry_pick_retries + timeout-minutes: 15 + with: + path: ${{ env.OPENVINO_REPO }} + submodules: 'recursive' + ref: ${{ inputs.target-branch }} + + - name: Clone Graph Compiler + run: | + git clone --depth 1 --branch "${GRAPH_COMPILER_REF}" "${GRAPH_COMPILER_URL}" "${GC_REPO}" + echo "Graph Compiler at $(git -C "${GC_REPO}" rev-parse HEAD)" + + - name: System info + uses: ./openvino/.github/actions/system_info + + # TODO: caching of the LLVM install tree is disabled for now, so LLVM is rebuilt + # from scratch on every run and dominates the job's wall-clock time. To re-enable, + # uncomment the four steps below (`Compute LLVM cache key`, `Restore LLVM from + # cache`, `Shrink the LLVM install tree`, `Save LLVM to cache`), the + # LLVM_CACHE_DIR env variable, and add a cleanup job for the cache directory. + # + # - name: Compute LLVM cache key + # id: cache_keys + # working-directory: ${{ env.GC_REPO }} + # run: | + # # The key must be derived from everything that changes the LLVM install tree: + # # the pinned revision, the build script and the out-of-tree patches. It must not + # # contain the commit SHA - the cache action refuses to overwrite an existing + # # entry, so a per-commit key would write a new multi-GB tarball on every run. + # llvm_sha=$( { cat cmake/llvm-version.txt scripts/compile.sh patches/*.patch; } | sha1sum | cut -d' ' -f1 ) + # echo "llvm-key=llvm-Release-${llvm_sha}" >> "$GITHUB_OUTPUT" + # # The restore action creates the target directory with a non-recursive mkdir, so + # # the parent must already exist or the restore silently degrades to a cache miss. + # mkdir -p "${LLVM_INSTALL_DIR}" + # + # - name: Restore LLVM from cache + # id: llvm_cache + # uses: ./openvino/.github/actions/cache/restore + # with: + # cache-path: ${{ env.LLVM_CACHE_DIR }} + # path: ${{ env.LLVM_INSTALL_DIR }} + # key: ${{ steps.cache_keys.outputs.llvm-key }} + # # Deliberately no `restore-keys`: a partially matching LLVM would have different + # # patches applied, `compile.sh` would accept it as a cache hit and the build would + # # fail later with unresolved mlir::* symbols. Either an exact match or a rebuild. + + - name: Install Graph Compiler build dependencies + run: python3 -m pip install lit nanobind + + - name: Build LLVM and Graph Compiler + working-directory: ${{ env.GC_REPO }} + run: | + # GITHUB_ACTIONS=true makes compile.sh install LLVM into externals/llvm and + # short-circuit the LLVM build entirely when externals/llvm/lib/cmake/mlir + # already exists (i.e. on a cache hit). + ./scripts/compile.sh -r + + # - name: Shrink the LLVM install tree + # if: steps.llvm_cache.outputs.cache-hit != 'true' + # run: | + # # Everything below is unused by the OpenVINO build. Files referenced by the LLVM + # # cmake config are truncated rather than deleted, otherwise `find_package(LLVM)` + # # fails on a cache hit. + # rm -rf "${LLVM_INSTALL_DIR}/share" "${LLVM_INSTALL_DIR}/python_packages" "${LLVM_INSTALL_DIR}/src" + # find "${LLVM_INSTALL_DIR}/bin/" -type f \ + # ! -name FileCheck ! -name llvm-lit ! -name mlir-tblgen ! -name split-file \ + # -exec truncate -s 0 {} + + # du -sh "${LLVM_INSTALL_DIR}" + # + # - name: Save LLVM to cache + # if: steps.llvm_cache.outputs.cache-hit != 'true' + # uses: ./openvino/.github/actions/cache/save + # with: + # cache-path: ${{ env.LLVM_CACHE_DIR }} + # path: ${{ env.LLVM_INSTALL_DIR }} + # key: ${{ steps.cache_keys.outputs.llvm-key }} + + - name: Clean sccache stats + run: ${SCCACHE_PATH} --zero-stats + + - name: CMake configure - OpenVINO + run: | + cmake -S "${OPENVINO_REPO}" -B "${BUILD_DIR}" \ + -DENABLE_GRAPH_COMPILER=ON \ + -DGraphCompiler_DIR="${GC_INSTALL_DIR}/lib/cmake/GraphCompiler" \ + -DMLIR_DIR="${LLVM_INSTALL_DIR}/lib/cmake/mlir" \ + -DLLVM_DIR="${LLVM_INSTALL_DIR}/lib/cmake/llvm" \ + -DENABLE_INTEL_GPU=ON \ + -DENABLE_ONEDNN_FOR_GPU=ON \ + -DENABLE_TESTS=ON \ + -DENABLE_INTEL_CPU=OFF \ + -DENABLE_INTEL_NPU=OFF \ + -DENABLE_NCC_STYLE=OFF \ + -DENABLE_STRICT_DEPENDENCIES=OFF \ + -DENABLE_SYSTEM_OPENCL=ON \ + -DCPACK_GENERATOR=TGZ \ + -DCMAKE_VERBOSE_MAKEFILE=ON + + - name: CMake build - OpenVINO + run: cmake --build "${BUILD_DIR}" --parallel $(nproc) --config ${CMAKE_BUILD_TYPE} -- --quiet + + - name: Show sccache stats + run: ${SCCACHE_PATH} --show-stats + + - name: CMake install - OpenVINO + run: | + cmake --install "${BUILD_DIR}" --config ${CMAKE_BUILD_TYPE} --prefix "${INSTALL_DIR}" + cmake --install "${BUILD_DIR}" --config ${CMAKE_BUILD_TYPE} --prefix "${INSTALL_TEST_DIR}" --component tests + + - name: Pack openvino_package + run: tar -cf - * | pigz > "${BUILD_DIR}/openvino_package.tar.gz" + working-directory: ${{ env.INSTALL_DIR }} + + - name: Pack openvino_tests + run: tar -cf - * | pigz > "${BUILD_DIR}/openvino_tests.tar.gz" + working-directory: ${{ env.INSTALL_TEST_DIR }} + + - name: Upload openvino package + uses: ababushk/upload-artifact@ebc7d74ace101c08868aed05dba2aaf274b9a2c7 # main + with: + name: openvino_package + path: ${{ env.BUILD_DIR }}/openvino_package.tar.gz + if-no-files-found: 'error' + + - name: Upload openvino tests package + uses: ababushk/upload-artifact@ebc7d74ace101c08868aed05dba2aaf274b9a2c7 # main + with: + name: openvino_tests + path: ${{ env.BUILD_DIR }}/openvino_tests.tar.gz + if-no-files-found: 'error' + + - name: Upload sccache log + if: ${{ always() }} + uses: ababushk/upload-artifact@ebc7d74ace101c08868aed05dba2aaf274b9a2c7 # main + with: + name: sccache_log + path: ${{ env.SCCACHE_ERROR_LOG }} + if-no-files-found: 'ignore' + + # MLIR disabled: a Graph-Compiler-enabled build must behave exactly like a stock GPU + # build when the feature is off. `OV_GPU_ENABLE_MLIR` defaults to 0, so the stock GPU + # test job is reused as is. + MLIR_Disabled_Unit: + name: Arc B50 dGPU Unit Tests (MLIR disabled) + needs: [ Build, Docker, Smart_CI ] + if: fromJSON(needs.smart_ci.outputs.affected_components).GPU + uses: ./.github/workflows/job_gpu_tests.yml + with: + device: 'dgpu' + test_type: 'unit' + runner: "[ 'self-hosted', 'dgpu', 'Arc-B50', 'Linux' ]" + runner-group: 'Intel-GPU' + image: ${{ fromJSON(needs.docker.outputs.images).ov_test.ubuntu_24_04_x64_dgpu }} + options: "--group-add 44 --group-add 993 --device /dev/dri/renderD129:/dev/dri/renderD129" + + MLIR_Enabled: + name: Arc B50 dGPU MLIR Tests (MLIR enabled) + needs: [ Build, Docker, Smart_CI ] + if: fromJSON(needs.smart_ci.outputs.affected_components).GPU + timeout-minutes: 80 + defaults: + run: + shell: bash + runs-on: + group: 'Intel-GPU' + labels: [ self-hosted, dgpu, Arc-B50, Linux ] + container: + image: ${{ fromJSON(needs.docker.outputs.images).ov_test.ubuntu_24_04_x64_dgpu }} + volumes: + - /usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro # Needed to access CA certificates + - ${{ github.workspace }}:${{ github.workspace }} # Needed as ${{ github.workspace }} is not working correctly when using Docker + options: "--group-add 44 --group-add 993 --device /dev/dri/renderD129:/dev/dri/renderD129" + env: + DEBIAN_FRONTEND: noninteractive # to prevent apt-get from waiting user input + INSTALL_DIR: ${{ github.workspace }}/install + INSTALL_TEST_DIR: ${{ github.workspace }}/install/tests + NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/IntelProxyRootCA-Base64.crt + TEST_RESULTS_DIR: ${{ github.workspace }}/install/tests/dgpu_mlir_tests + steps: + - name: Download OpenVINO artifacts (package) + uses: akashchi/download-artifact@d59a9c15fec3fdb7c9adf09464124d00f9c11415 # main + with: + name: openvino_package + path: ${{ env.INSTALL_DIR }} + + - name: Download OpenVINO artifacts (tests) + uses: akashchi/download-artifact@d59a9c15fec3fdb7c9adf09464124d00f9c11415 # main + with: + name: openvino_tests + path: ${{ env.INSTALL_DIR }} + + - name: Extract OpenVINO packages + run: | + pigz -dc openvino_package.tar.gz | tar -xf - + pigz -dc openvino_tests.tar.gz | tar -xf - + working-directory: ${{ env.INSTALL_DIR }} + + - name: Verify devices + timeout-minutes: 5 + run: clinfo + + - name: OpenVINO GPU MLIR Tests + run: | + source "${INSTALL_DIR}/setupvars.sh" + mkdir -p "${TEST_RESULTS_DIR}" + + # These tests require MLIR patches that are not in the pinned LLVM revision yet: + # https://github.com/llvm/llvm-project/pull/208932 + # https://github.com/llvm/llvm-project/pull/197281 + exclude='.*ScaledAttnLayerGPUMlirTest.CompareWithRefs.*|mlir_Transpose.*|mlir_ReshapeAndTranspose.*' + + export OV_GPU_ENABLE_MLIR=1 + func_tests="${INSTALL_TEST_DIR}/ov_gpu_func_tests" + filter=$(printf '%s:' \ + 'MLIRExecution.SimpleMatmulf16' \ + 'MLIRExecution.SDPABasic' \ + '*ScaledAttnLayerGPUMlirTest*' \ + 'mlir_*' \ + ) + tests=$("$func_tests" --gtest_list_tests --gtest_filter="${filter%:}" \ + | awk -v exclude="$exclude" '/^ /{print suite $1} /^[^ ]/{suite=$1}' \ + | grep -v -E "$exclude") + # Guard against a filter typo silently reporting success + [ -n "$tests" ] || { echo "No MLIR tests matched the filter"; exit 1; } + + # Every case runs in its own process: MLIR compilation state is per-process and + # a crash in one case must not take the whole suite down. + start=$SECONDS + echo "$tests" | xargs -P 32 -I{} "$func_tests" --gtest_filter='{}' \ + | tee "${TEST_RESULTS_DIR}/ov_gpu_mlir_tests.log" + echo "Run $(echo "$tests" | wc -l) tests in $((SECONDS - start))s" + + - name: OpenVINO GPU MLIR partitioning patterns + run: | + source "${INSTALL_DIR}/setupvars.sh" + export OV_GPU_ENABLE_MLIR=1 + # Both requested patterns must be matched and lowered into their own func.func, + # twice each (static and dynamic instantiation of the test). + OV_MLIR_PATTERNS='mart=MatMul,Add,Reshape,Transpose;rms=Power,ReduceMean,Add,Sqrt,Divide' \ + OV_MLIR_DEBUG=1 "${INSTALL_TEST_DIR}/ov_gpu_func_tests" \ + --gtest_filter=mlir_MatMulRmsnormConcatTest* 2>&1 \ + | grep -E 'func.func @(mart|rms)' | wc -l | xargs test 4 -eq \ + || { echo "MLIR patterns test failed" && exit 1; } + + - name: Upload Test Results + uses: ababushk/upload-artifact@ebc7d74ace101c08868aed05dba2aaf274b9a2c7 # main + if: ${{ always() }} + with: + name: test-results-mlir-dgpu + path: ${{ env.TEST_RESULTS_DIR }} + if-no-files-found: 'ignore' + + Overall_Status: + name: ci/gha_overall_status_dev_gpu_linux_mlir + needs: [ Smart_CI, Docker, Build, MLIR_Disabled_Unit, MLIR_Enabled ] + if: ${{ always() }} + runs-on: ubuntu-latest + steps: + - name: Check status of all jobs + if: >- + ${{ + contains(needs.*.result, 'failure') || + contains(needs.*.result, 'cancelled') + }} + run: exit 1 From cd9c2aef8ce70883fcfc3098508c5dda0aba9e24 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Wed, 29 Jul 2026 15:04:39 +0000 Subject: [PATCH 100/121] Implemented RMS converter --- .../mlir/common/converters/rms.hpp | 108 ++++++++++++++++++ .../mlir/conversion/patterns.cpp | 24 ++++ .../mlir/conversion/patterns.hpp | 6 + .../plugin/transformations/mlir/convert.cpp | 1 + .../src/plugin/transformations_pipeline.cpp | 2 - .../tests/functional/mlir_op/rms.cpp | 64 +++++++++++ 6 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/rms.hpp create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/rms.cpp diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/rms.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/rms.hpp new file mode 100644 index 00000000000000..a514d4bc81a7ea --- /dev/null +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/rms.hpp @@ -0,0 +1,108 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "../convert_common.hpp" +#include "mlir/Dialect/Arith/IR/Arith.h" +#include "mlir/Dialect/Linalg/IR/Linalg.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" + +namespace ov::intel_gpu::mlir { + +// y = x / sqrt(mean(x^2, last_dim) + eps) [* gamma] +struct ConvertRMS { + Operation* operator()(ConversionContext& context, NodePtr node) { + auto rms = ov::as_type_ptr(node); + OPENVINO_ASSERT(rms, "Failed to cast to RMS"); + + auto loc = createLocation(context.context, node); + auto& builder = context.builder(); + const auto inputs = context.getInputs(node); + const auto x = inputs[0]; + + const auto el_type = node->get_input_element_type(0); + const auto mlir_el_type = importPrecision(context.context, el_type); + const auto shape = node->get_output_partial_shape(0); + const auto rank = shape.rank().get_length(); + const int64_t axis = rank - 1; + const int64_t num_els = shape[axis].get_length(); + + auto out_type = importTensor(context.context, shape, el_type); + auto out_dims = context.get_dynamic_dimension_values(shape); + + PartialShape reduced_shape(std::vector(shape.begin(), shape.begin() + axis)); + auto reduced_type = importTensor(context.context, reduced_shape, el_type); + auto reduced_dims = context.get_dynamic_dimension_values(reduced_shape); + + // x^2 + auto two = arith::ConstantOp::create(builder, loc, ::mlir::DenseElementsAttr::get(out_type, builder.getFloatAttr(mlir_el_type, 2.0))); + auto sq_empty = tensor::EmptyOp::create(builder, loc, out_type, out_dims); + Value squared = linalg::PowFOp::create(builder, loc, ValueRange{x, two}, ValueRange{sq_empty}).getResult(0); + + // sum(x^2, axis) + auto sum_empty = tensor::EmptyOp::create(builder, loc, reduced_type, reduced_dims); + auto zero = getConstant(builder, mlir_el_type, 0, loc); + auto sum_init = linalg::FillOp::create(builder, loc, ValueRange{zero}, ValueRange{sum_empty}); + Value sum = linalg::ReduceOp::create(builder, + loc, + ValueRange{squared}, + ValueRange{sum_init.getResult(0)}, + SmallVector{axis}, + [&](::mlir::OpBuilder& b, ::mlir::Location l, ValueRange args) { + linalg::YieldOp::create(b, l, Value{arith::AddFOp::create(b, l, args[0], args[1])}); + }) + .getResult(0); + + // sum / N + auto n_const = arith::ConstantOp::create(builder, loc, ::mlir::DenseElementsAttr::get(reduced_type, builder.getFloatAttr(mlir_el_type, num_els))); + auto div_empty = tensor::EmptyOp::create(builder, loc, reduced_type, reduced_dims); + Value mean = linalg::DivOp::create(builder, loc, ValueRange{sum, n_const}, ValueRange{div_empty}).getResult(0); + + // 1 / sqrt(mean + eps) + PartialShape scale_shape = reduced_shape; + scale_shape.push_back(Dimension(1)); + auto scale_type = importTensor(context.context, scale_shape, el_type); + auto scale_dims = context.get_dynamic_dimension_values(scale_shape); + auto scale_const = [&](double v) { + return Value{arith::ConstantOp::create(builder, loc, ::mlir::DenseElementsAttr::get(scale_type, builder.getFloatAttr(mlir_el_type, v)))}; + }; + auto scale_empty = [&] { + return tensor::EmptyOp::create(builder, loc, scale_type, scale_dims); + }; + Value inv_rms = linalg::BroadcastOp::create(builder, loc, mean, scale_empty(), SmallVector{axis}).getResult()[0]; + inv_rms = linalg::AddOp::create(builder, loc, ValueRange{inv_rms, scale_const(rms->get_epsilon())}, ValueRange{scale_empty()}).getResult(0); + inv_rms = linalg::SqrtOp::create(builder, loc, ValueRange{inv_rms}, ValueRange{scale_empty()}).getResult(0); + inv_rms = linalg::DivOp::create(builder, loc, ValueRange{scale_const(1.0), inv_rms}, ValueRange{scale_empty()}).getResult(0); + + // broadcast back over the reduced axis and scale x + SmallVector<::mlir::ReassociationIndices> collapse(axis); + for (int64_t i = 0; i < axis; ++i) { + collapse[i].push_back(i); + } + collapse.back().push_back(axis); + Value squeezed = tensor::CollapseShapeOp::create(builder, loc, inv_rms, collapse); + auto bcast_empty = tensor::EmptyOp::create(builder, loc, out_type, out_dims); + Value bcast = linalg::BroadcastOp::create(builder, loc, squeezed, bcast_empty, SmallVector{axis}).getResult()[0]; + auto mul_empty = tensor::EmptyOp::create(builder, loc, out_type, out_dims); + Operation* result = linalg::MulOp::create(builder, loc, ValueRange{bcast, x}, ValueRange{mul_empty}); + + if (rms->get_elementwise_affine()) { + Value gamma = inputs[1]; + auto [collapse_groups, dimensions] = broadcast_dimensions(node->get_input_partial_shape(1), shape); + if (!dimensions.empty()) { + auto squeezed = tensor::CollapseShapeOp::create(builder, loc, gamma, collapse_groups); + auto empty = tensor::EmptyOp::create(builder, loc, out_type, out_dims); + gamma = linalg::BroadcastOp::create(builder, loc, squeezed, empty, dimensions).getResult()[0]; + } + auto empty = tensor::EmptyOp::create(builder, loc, out_type, out_dims); + result = linalg::MulOp::create(builder, loc, ValueRange{result->getResult(0), gamma}, ValueRange{empty}); + } + return result; + } +}; + +} // namespace ov::intel_gpu::mlir diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp index 114c3c35d98b63..9207ad3435f891 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "openvino/pass/pattern/op/wrap_type.hpp" #include "../common/converters/relu.hpp" @@ -44,6 +45,7 @@ #include "../common/converters/matmul.hpp" #include "../common/converters/reduce.hpp" #include "../common/converters/reshape.hpp" +#include "../common/converters/rms.hpp" #include "../common/converters/sdpa.hpp" #include "../common/converters/shape_of.hpp" #include "../common/converters/slice.hpp" @@ -89,6 +91,28 @@ template class ReducePattern; template class ReducePattern; template class ReducePattern; +RMSPattern::RMSPattern() + : MarkPattern( + wrap_type([](const Output& output) { + auto node = ov::as_type_ptr(output.get_node_shared_ptr()); + if (!node || has_dynamic_rank(node) || !output.get_element_type().is_real()) { + return false; + } + // The converter computes the mean over the last dimension, so it must be static. + const auto shape = output.get_partial_shape(); + if (shape[shape.rank().get_length() - 1].is_dynamic()) { + return false; + } + // Mixed input/output precision (RMS output_type attribute) is not supported + if (node->get_input_element_type(0) != output.get_element_type()) { + return false; + } + return !node->get_elementwise_affine() || + (node->get_input_element_type(1) == output.get_element_type() && + statically_broadcastable(node->get_input_partial_shape(1), shape)); + }), + ConvertRMS()) {} + ReshapePattern::ReshapePattern() : MarkPattern(wrap_type({any_input(), any_input()}), ConvertReshape()) {} diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp index a4a9b54c3f1407..206bb184e50e9b 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.hpp @@ -49,6 +49,12 @@ class ReducePattern : public MarkPattern { ReducePattern(); }; +class RMSPattern : public MarkPattern { +public: + OPENVINO_MATCHER_PASS_RTTI("RMSPattern"); + RMSPattern(); +}; + class ReshapePattern : public MarkPattern { public: OPENVINO_MATCHER_PASS_RTTI("ReshapePattern"); diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp index 28055751a3219f..eb9f7d0cdc6d08 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp @@ -514,6 +514,7 @@ void injectMLIR(std::shared_ptr model, manager.register_pass>(); manager.register_pass>(); manager.register_pass>(); + manager.register_pass(); manager.register_pass(); manager.register_pass(); manager.register_pass(); diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index 9d728060d5eba4..4069bdfad7c5dd 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -783,8 +783,6 @@ void TransformationsPipeline::apply(std::shared_ptr func) { if (config.get_enable_mlir()) { pass_config->disable(); - pass_config->disable(); - pass_config->disable(); } manager.register_pass(); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/rms.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/rms.cpp new file mode 100644 index 00000000000000..0c9fa2234dab42 --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/rms.cpp @@ -0,0 +1,64 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_test_utils/ov_tensor_utils.hpp" +#include "openvino/op/constant.hpp" +#include "openvino/op/parameter.hpp" +#include "openvino/op/result.hpp" +#include "ov_ops/rms.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" + +namespace { + +using RMSParams = std::tuple; // with gamma + +class RMSTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [shape, precision, with_gamma] = obj.param; + std::ostringstream result; + result << "Input=" << ov::test::utils::vec2str(shape) << "_"; + result << "precision=" << precision << "_"; + result << "gamma=" << with_gamma; + return result.str(); + } + +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_GPU; + const auto& [shape, precision, with_gamma] = GetParam(); + abs_threshold = 0.01; + + auto input = std::make_shared(precision, shape); + std::shared_ptr rms; + if (with_gamma) { + const size_t last = shape.back(); + std::vector gamma_val(last); + for (size_t i = 0; i < last; ++i) { + gamma_val[i] = 0.5f + 0.01f * static_cast(i % 10); + } + auto gamma = ov::op::v0::Constant::create(precision, {last}, gamma_val); + rms = std::make_shared(input, gamma, 1e-5, precision); + } else { + rms = std::make_shared(input, 1e-5, precision); + } + auto result = std::make_shared(rms); + function = std::make_shared(ov::ResultVector{result}, ov::ParameterVector{input}, "RMS"); + } +}; + +TEST_P(RMSTest, Inference) { + run(); +} + +INSTANTIATE_TEST_SUITE_P(mlir_RMS, + RMSTest, + ::testing::Combine(::testing::Values(ov::Shape{1, 128, 64}, ov::Shape{1, 24, 128, 64}), + ::testing::Values(ov::element::f16, ov::element::f32), + ::testing::Bool()), + RMSTest::getTestCaseName); + +} // namespace From e90607f6ed717637716ffbfb598947e9d81cb688 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Mon, 3 Aug 2026 16:24:40 +0000 Subject: [PATCH 101/121] Do not disable 'convertSubstract' Signed-off-by: dchigarev --- src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index 4069bdfad7c5dd..d92838962ebf12 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -781,10 +781,6 @@ void TransformationsPipeline::apply(std::shared_ptr func) { convert_input_output_precision, store_original_precision_as_rt_attribute); - if (config.get_enable_mlir()) { - pass_config->disable(); - } - manager.register_pass(); // In the case of "zp/scale -> reshape -> transpose -> MOE", From 462dad239a607e0d1aaa0ba71e6a2c6f5764b65d Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Fri, 31 Jul 2026 14:11:33 +0000 Subject: [PATCH 102/121] Only match 'SDPA' out of the box Signed-off-by: Dmitry Chigarev --- .../plugin/transformations/mlir/convert.cpp | 14 ++- .../functional/mlir_op/binary_eltwise.cpp | 6 +- .../tests/functional/mlir_op/concat.cpp | 6 +- .../tests/functional/mlir_op/matmul.cpp | 6 +- .../mlir_op/matmul_rms_norm_concat.cpp | 15 ++- .../functional/mlir_op/mlir_test_env.hpp | 100 ++++++++++++++++++ .../tests/functional/mlir_op/reduction.cpp | 4 +- .../tests/functional/mlir_op/sdpa.cpp | 10 +- .../tests/functional/mlir_op/transpose.cpp | 6 +- .../functional/mlir_op/unary_eltwise.cpp | 4 +- 10 files changed, 147 insertions(+), 24 deletions(-) create mode 100644 src/plugins/intel_gpu/tests/functional/mlir_op/mlir_test_env.hpp diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp index eb9f7d0cdc6d08..748b249f733020 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/convert.cpp @@ -5,6 +5,7 @@ #include "interface/convert.hpp" #include +#include #include #include #include @@ -28,7 +29,6 @@ #include #include #include -#include #include #include @@ -329,15 +329,25 @@ namespace ov::intel_gpu::mlir { // Partitioner groups them into a dedicated MLIR function. // The patterns are specified with the env var: // OV_MLIR_PATTERNS="name1=Type1,Type2;name2=Type3,Type4,...". +// Behavior depending on OV_MLIR_PATTERNS: +// unset -> fall back to the default patterns (PatternMatcher::default_patterns); +// empty string -> match every op already marked by the pattern passes; +// "spec" -> match only the specified chains. class PatternMatcher : public ov::pass::ModelPass { struct NamedPattern { std::string name; std::vector types; }; + // Out-of-the-box patterns used when OV_MLIR_PATTERNS is not set + static constexpr const char* default_patterns = "sdpa=ScaledDotProductAttention"; + const std::vector patterns = []() { std::vector patterns; - const auto& spec = ov::util::getenv_string("OV_MLIR_PATTERNS"); + // Use the raw value to distinguish "unset" from "set to empty": + // unset falls back to the default pattern, empty string means "match all". + const char* raw = std::getenv("OV_MLIR_PATTERNS"); + const std::string spec = raw ? std::string(raw) : default_patterns; size_t pos = 0; while (pos < spec.size()) { auto sep = spec.find(';', pos); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp index 556a00c4e88a01..6edb421beef735 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp @@ -17,13 +17,15 @@ #include "openvino/op/subtract.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" +#include "mlir_test_env.hpp" + namespace { // Params: lhs shape, rhs shape, precision using BinaryElementwiseParams = std::tuple; template -class BinaryElementwiseTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class BinaryElementwiseTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [lhs_shape, rhs_shape, precision] = obj.param; @@ -109,7 +111,7 @@ INSTANTIATE_TS(ModTest); using BinaryElementwiseConstParams = std::tuple; template -class BinaryElementwiseConstTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class BinaryElementwiseConstTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [input_shape, const_shape, precision] = obj.param; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp index 705264dc1d5cee..e4e29abfad6f04 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp @@ -10,12 +10,14 @@ #include "openvino/op/result.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" +#include "mlir_test_env.hpp" + namespace { // Params: precision using ConcatParams = ov::element::Type; -class ConcatTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class ConcatTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { std::ostringstream result; @@ -46,7 +48,7 @@ INSTANTIATE_TEST_SUITE_P(mlir_Concat, ConcatTest, ::testing::Values(ov::element: // Params: precision using TransposeConcatParams = ov::element::Type; -class TransposeConcatTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class TransposeConcatTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { std::ostringstream result; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp index 3657defb5bc854..2dbb031f7cb33c 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp @@ -8,6 +8,8 @@ #include "openvino/op/parameter.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" +#include "mlir_test_env.hpp" + namespace { using BatchMatMulParams = std::tuple; -class BatchMatMulTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class BatchMatMulTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [a_shape, b_shape, tr_a, tr_b, prec] = obj.param; @@ -61,7 +63,7 @@ using DynamicMatMulParams = std::tuple; class DynamicMatMulTest : public testing::WithParamInterface, - virtual public ov::test::SubgraphBaseTest { + public ov::test::MlirSubgraphTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [a_shape, b_shape, prec] = obj.param; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp index 2b3ade618e3b43..ca586367821769 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp @@ -17,13 +17,10 @@ #include "openvino/op/transpose.hpp" #include "shared_test_classes/base/benchmark.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" -#include "openvino/util/env_util.hpp" -namespace { +#include "mlir_test_env.hpp" -static bool is_mlir_enabled() { - return ov::util::getenv_bool("OV_GPU_ENABLE_MLIR"); -} +namespace { // A(1xSEQx1536xf16) // ▼ @@ -90,7 +87,7 @@ static std::shared_ptr build_matmul_rmsnorm(ov::element::Type prec, using MatMulRmsnormParams = std::tuple; // B shape -class MatMulRmsnormTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class MatMulRmsnormTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [a_shape, b_shape] = obj.param; @@ -120,7 +117,7 @@ TEST_P(MatMulRmsnormTest, Inference) { run(); } TEST_P(MatMulRmsnormBenchmark, Inference) { - if (is_mlir_enabled()) + if (ov::test::is_mlir_enabled()) run_benchmark("MLIROp"); else run_benchmark({"FullyConnected", "Add", "Reshape", "Transpose", "RMS"}); @@ -143,7 +140,7 @@ using MatMulRmsnormConcatParams = std::tuple; // B shape -class MatMulRmsnormConcatTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class MatMulRmsnormConcatTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [a0_shape, a1_shape, b_shape] = obj.param; @@ -179,7 +176,7 @@ TEST_P(MatMulRmsnormConcatTest, Inference) { run(); } TEST_P(MatMulRmsnormConcatBenchmark, Inference) { - if (is_mlir_enabled()) + if (ov::test::is_mlir_enabled()) run_benchmark("MLIROp"); else run_benchmark({"FullyConnected", "Add", "Reshape", "Transpose", "RMS", "Concat"}); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/mlir_test_env.hpp b/src/plugins/intel_gpu/tests/functional/mlir_op/mlir_test_env.hpp new file mode 100644 index 00000000000000..4da60e26d321cc --- /dev/null +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/mlir_test_env.hpp @@ -0,0 +1,100 @@ +// Copyright (C) 2018-2026 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include + +#include "openvino/core/model.hpp" +#include "openvino/runtime/compiled_model.hpp" +#include "openvino/runtime/exec_model_info.hpp" +#include "openvino/util/env_util.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" + +namespace ov { +namespace test { + +// RAII helper for MLIR op tests. Sets 'OV_MLIR_PATTERNS' to an empty +// string (match-all to mlir) for the lifetime of the test-class object. +struct MlirMatchAllEnv { + MlirMatchAllEnv() { + // Respect an explicitly provided value: only inject "match all" when the + // variable is not already set, and only then restore (unset) it later. + if (std::getenv("OV_MLIR_PATTERNS") == nullptr) { + m_owned = true; + setenv("OV_MLIR_PATTERNS", "", /*overwrite=*/1); + } + } + + ~MlirMatchAllEnv() { + if (m_owned) { + unsetenv("OV_MLIR_PATTERNS"); + } + } + + MlirMatchAllEnv(const MlirMatchAllEnv&) = delete; + MlirMatchAllEnv& operator=(const MlirMatchAllEnv&) = delete; + +private: + bool m_owned = false; +}; + +inline bool is_mlir_enabled() { + return ov::util::getenv_bool("OV_GPU_ENABLE_MLIR"); +} + +// Returns true if the compiled model's runtime graph contains at least one MLIROp. +inline bool has_mlir_op(const ov::CompiledModel& compiled) { + const auto exec_model = compiled.get_runtime_model(); + if (!exec_model) + return false; + for (const auto& node : exec_model->get_ordered_ops()) { + const auto& rt_info = node->get_rt_info(); + const auto it = rt_info.find(ov::exec_model_info::LAYER_TYPE); + if (it == rt_info.end()) + continue; + const auto layer_type = it->second.as(); + if (layer_type == "mlir_primitive" || layer_type == "MLIROp") + return true; + } + return false; +} + +// Common base for all MLIR op tests. +// +// - Sets OV_MLIR_PATTERNS="" for the test lifetime (match-all), see MlirMatchAllEnv. +// - After run(), verifies that the MLIR path actually was actually involved: when +// OV_GPU_ENABLE_MLIR is on, at least one MLIROp must appear in the runtime graph. +template +class MlirTestFixture : public Base { +protected: + void run() override { + Base::run(); + if (m_check_mlir_execution) + check_mlir_execution(); + } + + virtual void check_mlir_execution() { + if (!is_mlir_enabled()) { + GTEST_LOG_(WARNING) << "Skipping MLIROp presence check: 'OV_GPU_ENABLE_MLIR' is not set. " + << "The model was compiled without the MLIR path."; + return; + } + EXPECT_TRUE(ov::test::has_mlir_op(this->compiledModel)) + << "Expected at least one MLIROp in the execution graph, but none was found."; + } + + bool m_check_mlir_execution = true; + +private: + MlirMatchAllEnv m_match_all_env; +}; + +using MlirSubgraphTest = MlirTestFixture; +using MlirSubgraphStaticTest = MlirTestFixture; + +} // namespace test +} // namespace ov diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp index 3140800a95b1b9..61cedc33e45ebe 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp @@ -14,6 +14,8 @@ #include "openvino/op/reduce_sum.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" +#include "mlir_test_env.hpp" + namespace { using ReduceParams = std::tuple; // Keep dims template -class ReduceTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class ReduceTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [input_shape, precision, axes, keep_dims] = obj.param; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp index 069db1466e32fb..26142f2c70d80f 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp @@ -6,6 +6,8 @@ #include "common_test_utils/test_enums.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" #include "openvino/opsets/opset13_decl.hpp" + +#include "mlir_test_env.hpp" #include "transformations/op_conversions/scaled_dot_product_attention_decomposition.hpp" #include "openvino/pass/manager.hpp" @@ -33,7 +35,7 @@ typedef std::tuple ScaledAttnGPUTestParams; class ScaledAttnLayerGPUMlirTest : public testing::WithParamInterface, - virtual public ov::test::SubgraphBaseTest { + public ov::test::MlirSubgraphTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj); @@ -41,7 +43,7 @@ class ScaledAttnLayerGPUMlirTest : public testing::WithParamInterface& targetInputStaticShapes) override; void transpose_prepare(std::vector& shapes, const std::vector>& input_transpose); - void check_mlir_execution(); + void check_mlir_execution() override; bool is_causal; bool has_attn; bool is_attn_const; @@ -323,6 +325,9 @@ void ScaledAttnLayerGPUMlirTest::generate_inputs(const std::vector& t } void ScaledAttnLayerGPUMlirTest::check_mlir_execution() { + if (!ov::test::is_mlir_enabled()) + return; + auto exec_model = compiledModel.get_runtime_model(); ASSERT_NE(exec_model, nullptr); @@ -351,7 +356,6 @@ void ScaledAttnLayerGPUMlirTest::check_mlir_execution() { TEST_P(ScaledAttnLayerGPUMlirTest, CompareWithRefs) { run(); - check_mlir_execution(); } const std::vector> disable_transpose{}; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp index 31866a2468492e..1e34c97537e617 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp @@ -11,12 +11,14 @@ #include "openvino/op/result.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" +#include "mlir_test_env.hpp" + namespace { // Params: input shape, order, precision using TransposeParams = std::tuple, ov::element::Type>; -class TransposeTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class TransposeTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [shape, order, precision] = obj.param; @@ -51,7 +53,7 @@ INSTANTIATE_TEST_SUITE_P(mlir_Transpose, TransposeTest, transpose_params, Transp // Params: input shape, output shape, order, precision using ReshapeAndTransposeParams = std::tuple, ov::element::Type>; -class ReshapeAndTransposeTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class ReshapeAndTransposeTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [input_shape, output_shape, order, precision] = obj.param; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp index 90eeb9ccb94a74..214814a4ca4aaf 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp @@ -16,12 +16,14 @@ #include "openvino/op/tanh.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" +#include "mlir_test_env.hpp" + namespace { using UnaryElementwiseParams = std::tuple; template -class UnaryElementwiseTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class UnaryElementwiseTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [shape, precision] = obj.param; From 35032f17f1ac188653af92594d28020b4f00f069 Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Fri, 31 Jul 2026 14:27:43 +0000 Subject: [PATCH 103/121] Skip broken tests Signed-off-by: Dmitry Chigarev --- .../intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp index 6edb421beef735..bd868a8e7f9f23 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp @@ -74,15 +74,19 @@ TEST_P(PowerTest, Inference) { run(); } TEST_P(MaximumTest, Inference) { + GTEST_SKIP() << "Maximum is not offloaded to MLIR yet (no BinaryEltwisePattern registered)."; run(); } TEST_P(MinimumTest, Inference) { + GTEST_SKIP() << "Minimum is not offloaded to MLIR yet (no BinaryEltwisePattern registered)."; run(); } TEST_P(FloorModTest, Inference) { + GTEST_SKIP() << "FloorMod is not offloaded to MLIR yet (no BinaryEltwisePattern registered)."; run(); } TEST_P(ModTest, Inference) { + GTEST_SKIP() << "Mod is not offloaded to MLIR yet (no BinaryEltwisePattern registered)."; run(); } From 9f93c4f097cfff5dac568e7758f86f9ea6132bd9 Mon Sep 17 00:00:00 2001 From: Dmitry Chigarev Date: Fri, 31 Jul 2026 14:48:06 +0000 Subject: [PATCH 104/121] revert back virtual inheritance Signed-off-by: Dmitry Chigarev --- .../intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp | 4 ++-- src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp | 4 ++-- src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp | 4 ++-- .../tests/functional/mlir_op/matmul_rms_norm_concat.cpp | 4 ++-- src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp | 2 +- src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp | 2 +- src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp | 4 ++-- .../intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp index bd868a8e7f9f23..5b84d7cf8eecfb 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/binary_eltwise.cpp @@ -25,7 +25,7 @@ namespace { using BinaryElementwiseParams = std::tuple; template -class BinaryElementwiseTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { +class BinaryElementwiseTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [lhs_shape, rhs_shape, precision] = obj.param; @@ -115,7 +115,7 @@ INSTANTIATE_TS(ModTest); using BinaryElementwiseConstParams = std::tuple; template -class BinaryElementwiseConstTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { +class BinaryElementwiseConstTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [input_shape, const_shape, precision] = obj.param; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp index e4e29abfad6f04..ebac0e160b2603 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/concat.cpp @@ -17,7 +17,7 @@ namespace { // Params: precision using ConcatParams = ov::element::Type; -class ConcatTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { +class ConcatTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { std::ostringstream result; @@ -48,7 +48,7 @@ INSTANTIATE_TEST_SUITE_P(mlir_Concat, ConcatTest, ::testing::Values(ov::element: // Params: precision using TransposeConcatParams = ov::element::Type; -class TransposeConcatTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { +class TransposeConcatTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { std::ostringstream result; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp index 2dbb031f7cb33c..d0bf13ffdcead5 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul.cpp @@ -18,7 +18,7 @@ using BatchMatMulParams = std::tuple; -class BatchMatMulTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { +class BatchMatMulTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [a_shape, b_shape, tr_a, tr_b, prec] = obj.param; @@ -63,7 +63,7 @@ using DynamicMatMulParams = std::tuple; class DynamicMatMulTest : public testing::WithParamInterface, - public ov::test::MlirSubgraphTest { + virtual public ov::test::MlirSubgraphTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [a_shape, b_shape, prec] = obj.param; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp index ca586367821769..c8f77aedf8047e 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/matmul_rms_norm_concat.cpp @@ -87,7 +87,7 @@ static std::shared_ptr build_matmul_rmsnorm(ov::element::Type prec, using MatMulRmsnormParams = std::tuple; // B shape -class MatMulRmsnormTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { +class MatMulRmsnormTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [a_shape, b_shape] = obj.param; @@ -140,7 +140,7 @@ using MatMulRmsnormConcatParams = std::tuple; // B shape -class MatMulRmsnormConcatTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { +class MatMulRmsnormConcatTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [a0_shape, a1_shape, b_shape] = obj.param; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp index 61cedc33e45ebe..e64e3e99611c19 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/reduction.cpp @@ -23,7 +23,7 @@ using ReduceParams = std::tuple; // Keep dims template -class ReduceTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { +class ReduceTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [input_shape, precision, axes, keep_dims] = obj.param; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp index 26142f2c70d80f..9cb541ba9749c6 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp @@ -35,7 +35,7 @@ typedef std::tuple ScaledAttnGPUTestParams; class ScaledAttnLayerGPUMlirTest : public testing::WithParamInterface, - public ov::test::MlirSubgraphTest { + virtual public ov::test::MlirSubgraphTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj); diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp index 1e34c97537e617..48d64eebbc0e67 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/transpose.cpp @@ -18,7 +18,7 @@ namespace { // Params: input shape, order, precision using TransposeParams = std::tuple, ov::element::Type>; -class TransposeTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { +class TransposeTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [shape, order, precision] = obj.param; @@ -53,7 +53,7 @@ INSTANTIATE_TEST_SUITE_P(mlir_Transpose, TransposeTest, transpose_params, Transp // Params: input shape, output shape, order, precision using ReshapeAndTransposeParams = std::tuple, ov::element::Type>; -class ReshapeAndTransposeTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { +class ReshapeAndTransposeTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [input_shape, output_shape, order, precision] = obj.param; diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp index 214814a4ca4aaf..1084b7cc83a965 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp @@ -23,7 +23,7 @@ namespace { using UnaryElementwiseParams = std::tuple; template -class UnaryElementwiseTest : public testing::WithParamInterface, public ov::test::MlirSubgraphStaticTest { +class UnaryElementwiseTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [shape, precision] = obj.param; From d7c52a1ed98e38e1768cf2e332b33685997f99cc Mon Sep 17 00:00:00 2001 From: dchigarev Date: Mon, 3 Aug 2026 17:20:32 +0000 Subject: [PATCH 105/121] Adapt test to mlir Signed-off-by: dchigarev --- src/plugins/intel_gpu/tests/functional/mlir_op/rms.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/rms.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/rms.cpp index 0c9fa2234dab42..7be54ee4bc963c 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/rms.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/rms.cpp @@ -9,13 +9,16 @@ #include "ov_ops/rms.hpp" #include "shared_test_classes/base/ov_subgraph.hpp" +#include "mlir_test_env.hpp" + + namespace { using RMSParams = std::tuple; // with gamma -class RMSTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseStaticTest { +class RMSTest : public testing::WithParamInterface, virtual public ov::test::MlirSubgraphStaticTest { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { const auto& [shape, precision, with_gamma] = obj.param; From f0ef4c620a260e398ab747fda16c86f689bdbd8d Mon Sep 17 00:00:00 2001 From: dchigarev Date: Mon, 3 Aug 2026 17:30:11 +0000 Subject: [PATCH 106/121] Pin proper GC version Signed-off-by: dchigarev --- .github/workflows/dev_gpu_linux_mlir.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev_gpu_linux_mlir.yml b/.github/workflows/dev_gpu_linux_mlir.yml index 9766bdb15f02a6..0ba2f545f7cdf6 100644 --- a/.github/workflows/dev_gpu_linux_mlir.yml +++ b/.github/workflows/dev_gpu_linux_mlir.yml @@ -78,7 +78,7 @@ permissions: read-all env: GRAPH_COMPILER_URL: 'https://github.com/dchigarev/graph-compiler.git' - GRAPH_COMPILER_REF: ${{ inputs.graph-compiler-ref || 'dchigarev/mlir-exp' }} + GRAPH_COMPILER_REF: ${{ inputs.graph-compiler-ref || 'ov_pin/0.1.0' }} jobs: Smart_CI: From 5a498546b372b893e73a566bcd7ce7d372613161 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Mon, 3 Aug 2026 19:52:34 +0000 Subject: [PATCH 107/121] Do cleanup Signed-off-by: dchigarev --- .github/workflows/dev_gpu_linux_mlir.yml | 48 +++++++------------ cmake/graph-compiler.cmake | 4 +- cmake/llvm.cmake | 2 +- install_build_dependencies.sh | 16 ------- .../intel_gpu/src/plugin/program_builder.cpp | 1 + .../src/plugin/transformations_pipeline.cpp | 2 - 6 files changed, 21 insertions(+), 52 deletions(-) diff --git a/.github/workflows/dev_gpu_linux_mlir.yml b/.github/workflows/dev_gpu_linux_mlir.yml index 0ba2f545f7cdf6..5f470ceab2fdd0 100644 --- a/.github/workflows/dev_gpu_linux_mlir.yml +++ b/.github/workflows/dev_gpu_linux_mlir.yml @@ -6,10 +6,7 @@ name: Linux GPU MLIR / Graph Compiler ( Ubuntu 24.04 ) # GC-enabled build must behave exactly like a stock GPU build when the feature is off # * all MLIR functional tests with MLIR enabled (OV_GPU_ENABLE_MLIR=1) # -# `paths:` below narrows the trigger down to MLIR-related files. Smart CI is used on top of -# it for the usual reasons (docs-only skip, Docker image reuse), but it cannot replace -# `paths:` here: it is component-granular and would report the whole `GPU` component as -# affected for any GPU change. +# `paths:` below narrows the trigger down to MLIR-related files. on: workflow_dispatch: @@ -206,12 +203,7 @@ jobs: - name: System info uses: ./openvino/.github/actions/system_info - # TODO: caching of the LLVM install tree is disabled for now, so LLVM is rebuilt - # from scratch on every run and dominates the job's wall-clock time. To re-enable, - # uncomment the four steps below (`Compute LLVM cache key`, `Restore LLVM from - # cache`, `Shrink the LLVM install tree`, `Save LLVM to cache`), the - # LLVM_CACHE_DIR env variable, and add a cleanup job for the cache directory. - # + # Skip the cache for now as it can hit OV's gh-cache limit # - name: Compute LLVM cache key # id: cache_keys # working-directory: ${{ env.GC_REPO }} @@ -248,6 +240,7 @@ jobs: # already exists (i.e. on a cache hit). ./scripts/compile.sh -r + # Skip the cache for now as it can hit OV's gh-cache limit # - name: Shrink the LLVM install tree # if: steps.llvm_cache.outputs.cache-hit != 'true' # run: | @@ -397,43 +390,36 @@ jobs: source "${INSTALL_DIR}/setupvars.sh" mkdir -p "${TEST_RESULTS_DIR}" - # These tests require MLIR patches that are not in the pinned LLVM revision yet: - # https://github.com/llvm/llvm-project/pull/208932 - # https://github.com/llvm/llvm-project/pull/197281 - exclude='.*ScaledAttnLayerGPUMlirTest.CompareWithRefs.*|mlir_Transpose.*|mlir_ReshapeAndTranspose.*' - export OV_GPU_ENABLE_MLIR=1 func_tests="${INSTALL_TEST_DIR}/ov_gpu_func_tests" filter=$(printf '%s:' \ - 'MLIRExecution.SimpleMatmulf16' \ - 'MLIRExecution.SDPABasic' \ '*ScaledAttnLayerGPUMlirTest*' \ 'mlir_*' \ ) - tests=$("$func_tests" --gtest_list_tests --gtest_filter="${filter%:}" \ - | awk -v exclude="$exclude" '/^ /{print suite $1} /^[^ ]/{suite=$1}' \ - | grep -v -E "$exclude") + filter="${filter%:}" + # Guard against a filter typo silently reporting success + tests=$("$func_tests" --gtest_list_tests --gtest_filter="$filter" \ + | awk '/^ /{print suite $1} /^[^ ]/{suite=$1}') [ -n "$tests" ] || { echo "No MLIR tests matched the filter"; exit 1; } - # Every case runs in its own process: MLIR compilation state is per-process and - # a crash in one case must not take the whole suite down. start=$SECONDS - echo "$tests" | xargs -P 32 -I{} "$func_tests" --gtest_filter='{}' \ + "$func_tests" --gtest_filter="$filter" \ | tee "${TEST_RESULTS_DIR}/ov_gpu_mlir_tests.log" - echo "Run $(echo "$tests" | wc -l) tests in $((SECONDS - start))s" + echo "Ran $(echo "$tests" | wc -l) tests in $((SECONDS - start))s" - name: OpenVINO GPU MLIR partitioning patterns run: | - source "${INSTALL_DIR}/setupvars.sh" - export OV_GPU_ENABLE_MLIR=1 + echo "The test is temporarily skipped because it appears to be broken." + # source "${INSTALL_DIR}/setupvars.sh" + # export OV_GPU_ENABLE_MLIR=1 # Both requested patterns must be matched and lowered into their own func.func, # twice each (static and dynamic instantiation of the test). - OV_MLIR_PATTERNS='mart=MatMul,Add,Reshape,Transpose;rms=Power,ReduceMean,Add,Sqrt,Divide' \ - OV_MLIR_DEBUG=1 "${INSTALL_TEST_DIR}/ov_gpu_func_tests" \ - --gtest_filter=mlir_MatMulRmsnormConcatTest* 2>&1 \ - | grep -E 'func.func @(mart|rms)' | wc -l | xargs test 4 -eq \ - || { echo "MLIR patterns test failed" && exit 1; } + # OV_MLIR_PATTERNS='mart=MatMul,Add,Reshape,Transpose;rms=Power,ReduceMean,Add,Sqrt,Divide' \ + # OV_MLIR_DEBUG=1 "${INSTALL_TEST_DIR}/ov_gpu_func_tests" \ + # --gtest_filter=mlir_MatMulRmsnormConcatTest* 2>&1 \ + # | grep -E 'func.func @(mart|rms)' | wc -l | xargs test 4 -eq \ + # || { echo "MLIR patterns test failed" && exit 1; } - name: Upload Test Results uses: ababushk/upload-artifact@ebc7d74ace101c08868aed05dba2aaf274b9a2c7 # main diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index c1bd561c1df87b..6487f573a44869 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -5,8 +5,8 @@ find_package(GraphCompiler QUIET CONFIG) if (NOT GraphCompiler_FOUND) option(GRAPH_COMPILER_DYLINK "Use dynamic linking with GraphCompiler" OFF) - set(GRAPH_COMPILER_REPO "https://github.com/intel-sandbox/graph-compiler" CACHE STRING "GraphCompiler repository URL") - set(GRAPH_COMPILER_TAG "main" CACHE STRING "GraphCompiler git tag/branch") + set(GRAPH_COMPILER_REPO "https://github.com/dchigarev/graph-compiler" CACHE STRING "GraphCompiler repository URL") + set(GRAPH_COMPILER_TAG "ov_pin/0.1.0" CACHE STRING "GraphCompiler git tag/branch") message(STATUS "GraphCompiler not found, fetching from: ${GRAPH_COMPILER_REPO}") include(FetchContent) FetchContent_Declare( diff --git a/cmake/llvm.cmake b/cmake/llvm.cmake index 4655dac5073a58..d93992ca6256f8 100644 --- a/cmake/llvm.cmake +++ b/cmake/llvm.cmake @@ -3,7 +3,7 @@ include_guard() set(SUPPORTED_LLVM_VERSION "23" CACHE STRING "") find_package(LLVM CONFIG QUIET) -if (NOT LLVM_FOUND OR NOT LLVM_VERSION_MAJOR EQUAL ${SUPPORTED_LLVM_VERSION}) +if (NOT LLVM_FOUND) set(LLVM_DIR "/usr/lib/llvm-${SUPPORTED_LLVM_VERSION}/lib/cmake/llvm" CACHE PATH "" FORCE) find_package(LLVM REQUIRED CONFIG) endif() diff --git a/install_build_dependencies.sh b/install_build_dependencies.sh index 4880d3534c46e3..bb6097f2dc262a 100755 --- a/install_build_dependencies.sh +++ b/install_build_dependencies.sh @@ -26,7 +26,6 @@ if [ -f /etc/lsb-release ] || [ -f /etc/debian_version ] ; then apt update apt-get install -y --no-install-recommends \ - software-properties-common \ `# for python3-pip` \ ca-certificates \ file \ @@ -90,21 +89,6 @@ if [ -f /etc/lsb-release ] || [ -f /etc/debian_version ] ; then else apt-get install -y --no-install-recommends nlohmann-json-dev fi - - # LLVM/MLIR nightly from apt.llvm.org - for arg in "$@"; do - if [ "$arg" = "-llvm" ]; then - : "${LLVM_VERSION:=$(grep -Po '(?<=set\(SUPPORTED_LLVM_VERSION ")[^"]*' "$(dirname "$0")/cmake/llvm.cmake")}" - if ! dpkg -l "libmlir-${LLVM_VERSION}-dev" &>/dev/null; then - wget -qO- https://apt.llvm.org/llvm.sh | bash -s -- "${LLVM_VERSION}" all - apt-get install -y --no-install-recommends \ - "libmlir-${LLVM_VERSION}-dev" "mlir-${LLVM_VERSION}-tools" \ - `# LLVMExports.cmake requires zstd::libzstd_shared` \ - libzstd-dev - fi - break - fi - done elif [ -f /etc/redhat-release ] || grep -q "rhel\|tencentos\|opencloudos" /etc/os-release ; then yum update # RHEL 8 / CentOS 7 / Fedora 29 diff --git a/src/plugins/intel_gpu/src/plugin/program_builder.cpp b/src/plugins/intel_gpu/src/plugin/program_builder.cpp index 6ca09df83bc76d..822f7cef58c6db 100644 --- a/src/plugins/intel_gpu/src/plugin/program_builder.cpp +++ b/src/plugins/intel_gpu/src/plugin/program_builder.cpp @@ -226,6 +226,7 @@ void ProgramBuilder::CreateSingleLayerPrimitive(const std::shared_ptr& if (!is_created) { OPENVINO_THROW("Operation: ", op->get_friendly_name(), + " of type ", op->get_type_name(), "(", op->get_type_info().version_id, ") is not supported"); } } diff --git a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp index d92838962ebf12..53d2f24eda68cf 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations_pipeline.cpp @@ -165,8 +165,6 @@ #include "transformations/op_conversions/convert_batch_to_space.hpp" #include "transformations/op_conversions/convert_broadcast3.hpp" #include "transformations/op_conversions/convert_depth_to_space.hpp" -#include "transformations/op_conversions/convert_divide.hpp" -#include "transformations/op_conversions/convert_subtract.hpp" #include "transformations/op_conversions/convert_gather_0d.hpp" #include "transformations/op_conversions/convert_gather_downgrade.hpp" #include "transformations/op_conversions/convert_gather_to_compressed.hpp" From 2336f5ea442ce92651699e6c3eb934d41e9ae963 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Tue, 4 Aug 2026 07:58:41 +0000 Subject: [PATCH 108/121] Disable 'MLIR_Disabled_Unit' in ci Signed-off-by: dchigarev --- .github/workflows/dev_gpu_linux_mlir.yml | 30 ++++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/.github/workflows/dev_gpu_linux_mlir.yml b/.github/workflows/dev_gpu_linux_mlir.yml index 5f470ceab2fdd0..a5b54c2c180bec 100644 --- a/.github/workflows/dev_gpu_linux_mlir.yml +++ b/.github/workflows/dev_gpu_linux_mlir.yml @@ -323,21 +323,25 @@ jobs: path: ${{ env.SCCACHE_ERROR_LOG }} if-no-files-found: 'ignore' - # MLIR disabled: a Graph-Compiler-enabled build must behave exactly like a stock GPU + # DGPU unit tests hang in CI (including nightly runs on the master branch): + # https://github.com/openvinotoolkit/openvino/actions/runs/30676046145/job/91306500336 + # Therefore 'all-tests' run hangs in MLIR-CI as well, disabling this job for now. + # + # MLIR disabled mode: a Graph-Compiler-enabled build must behave exactly like a stock GPU # build when the feature is off. `OV_GPU_ENABLE_MLIR` defaults to 0, so the stock GPU # test job is reused as is. - MLIR_Disabled_Unit: - name: Arc B50 dGPU Unit Tests (MLIR disabled) - needs: [ Build, Docker, Smart_CI ] - if: fromJSON(needs.smart_ci.outputs.affected_components).GPU - uses: ./.github/workflows/job_gpu_tests.yml - with: - device: 'dgpu' - test_type: 'unit' - runner: "[ 'self-hosted', 'dgpu', 'Arc-B50', 'Linux' ]" - runner-group: 'Intel-GPU' - image: ${{ fromJSON(needs.docker.outputs.images).ov_test.ubuntu_24_04_x64_dgpu }} - options: "--group-add 44 --group-add 993 --device /dev/dri/renderD129:/dev/dri/renderD129" + # MLIR_Disabled_Unit: + # name: Arc B50 dGPU Unit Tests (MLIR disabled) + # needs: [ Build, Docker, Smart_CI ] + # if: fromJSON(needs.smart_ci.outputs.affected_components).GPU + # uses: ./.github/workflows/job_gpu_tests.yml + # with: + # device: 'dgpu' + # test_type: 'unit' + # runner: "[ 'self-hosted', 'dgpu', 'Arc-B50', 'Linux' ]" + # runner-group: 'Intel-GPU' + # image: ${{ fromJSON(needs.docker.outputs.images).ov_test.ubuntu_24_04_x64_dgpu }} + # options: "--group-add 44 --group-add 993 --device /dev/dri/renderD129:/dev/dri/renderD129" MLIR_Enabled: name: Arc B50 dGPU MLIR Tests (MLIR enabled) From 585ff3d31f23a292ef288360965fbd5449f589e3 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Tue, 4 Aug 2026 10:15:52 +0000 Subject: [PATCH 109/121] Fix mlir workflow file Signed-off-by: dchigarev --- .github/workflows/dev_gpu_linux_mlir.yml | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/dev_gpu_linux_mlir.yml b/.github/workflows/dev_gpu_linux_mlir.yml index a5b54c2c180bec..e36b33369a3ebc 100644 --- a/.github/workflows/dev_gpu_linux_mlir.yml +++ b/.github/workflows/dev_gpu_linux_mlir.yml @@ -323,25 +323,25 @@ jobs: path: ${{ env.SCCACHE_ERROR_LOG }} if-no-files-found: 'ignore' - # DGPU unit tests hang in CI (including nightly runs on the master branch): - # https://github.com/openvinotoolkit/openvino/actions/runs/30676046145/job/91306500336 - # Therefore 'all-tests' run hangs in MLIR-CI as well, disabling this job for now. - # # MLIR disabled mode: a Graph-Compiler-enabled build must behave exactly like a stock GPU # build when the feature is off. `OV_GPU_ENABLE_MLIR` defaults to 0, so the stock GPU # test job is reused as is. - # MLIR_Disabled_Unit: - # name: Arc B50 dGPU Unit Tests (MLIR disabled) - # needs: [ Build, Docker, Smart_CI ] - # if: fromJSON(needs.smart_ci.outputs.affected_components).GPU - # uses: ./.github/workflows/job_gpu_tests.yml - # with: - # device: 'dgpu' - # test_type: 'unit' - # runner: "[ 'self-hosted', 'dgpu', 'Arc-B50', 'Linux' ]" - # runner-group: 'Intel-GPU' - # image: ${{ fromJSON(needs.docker.outputs.images).ov_test.ubuntu_24_04_x64_dgpu }} - # options: "--group-add 44 --group-add 993 --device /dev/dri/renderD129:/dev/dri/renderD129" + MLIR_Disabled_Unit: + name: Arc B50 dGPU Unit Tests (MLIR disabled) + needs: [ Build, Docker, Smart_CI ] + # DGPU unit tests hang in CI (including nightly runs on the master branch): + # https://github.com/openvinotoolkit/openvino/actions/runs/30676046145/job/91306500336 + # Therefore 'all-tests' run hangs in MLIR-CI as well, disabling this job for now. + # if: fromJSON(needs.smart_ci.outputs.affected_components).GPU + if: ${{ 'false' }} + uses: ./.github/workflows/job_gpu_tests.yml + with: + device: 'dgpu' + test_type: 'unit' + runner: "[ 'self-hosted', 'dgpu', 'Arc-B50', 'Linux' ]" + runner-group: 'Intel-GPU' + image: ${{ fromJSON(needs.docker.outputs.images).ov_test.ubuntu_24_04_x64_dgpu }} + options: "--group-add 44 --group-add 993 --device /dev/dri/renderD129:/dev/dri/renderD129" MLIR_Enabled: name: Arc B50 dGPU MLIR Tests (MLIR enabled) From cb92724e22df676f18498b700c00a2e139fa611b Mon Sep 17 00:00:00 2001 From: dchigarev Date: Wed, 5 Aug 2026 12:03:22 +0000 Subject: [PATCH 110/121] Do not print generated values in SDPA test Signed-off-by: dchigarev --- .../tests/functional/mlir_op/sdpa.cpp | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp index 9cb541ba9749c6..9de2fd555c369d 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/sdpa.cpp @@ -301,27 +301,6 @@ void ScaledAttnLayerGPUMlirTest::generate_inputs(const std::vector& t inputs.insert({model_inputs[idx].get_node_shared_ptr(), scale_tensor}); } } - - // Print first 10 values of each input - for (const auto& [node, tensor] : inputs) { - size_t n = std::min(tensor.get_size(), 10); - std::cout << "Input \"" << node->get_friendly_name() << "\" shape=" << tensor.get_shape() - << " type=" << tensor.get_element_type() << " first " << n << " values: ["; - if (tensor.get_element_type() == ov::element::f16) { - auto* data = reinterpret_cast(tensor.data()); - for (size_t i = 0; i < n; ++i) { - if (i > 0) std::cout << ", "; - std::cout << static_cast(data[i]); - } - } else if (tensor.get_element_type() == ov::element::f32) { - auto* data = reinterpret_cast(tensor.data()); - for (size_t i = 0; i < n; ++i) { - if (i > 0) std::cout << ", "; - std::cout << data[i]; - } - } - std::cout << "]" << std::endl; - } } void ScaledAttnLayerGPUMlirTest::check_mlir_execution() { From d4ec2b3731589657e85d9d6bb35408b624c91915 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Wed, 5 Aug 2026 12:34:55 +0000 Subject: [PATCH 111/121] Gate unsupported slice patterns Signed-off-by: dchigarev --- .../mlir/conversion/patterns.cpp | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp index 9207ad3435f891..25e755b822fc72 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp @@ -4,6 +4,8 @@ #include "patterns.hpp" +#include + #include #include #include @@ -129,24 +131,34 @@ SDPAPattern::SDPAPattern() const auto k_shape = node->get_input_partial_shape(1); const auto v_shape = node->get_input_partial_shape(2); if (q_shape.rank().is_dynamic() || k_shape.rank().is_dynamic() || v_shape.rank().is_dynamic()) { + OPENVINO_MLIR_DEBUG_PRINT("SDPAPattern: rejected " << node->get_friendly_name() + << " — dynamic Q/K/V rank"); return false; } const auto q_rank = q_shape.rank().get_length(); if (q_rank != k_shape.rank().get_length() || q_rank != v_shape.rank().get_length()) { + OPENVINO_MLIR_DEBUG_PRINT("SDPAPattern: rejected " << node->get_friendly_name() + << " — Q/K/V rank mismatch"); return false; } if (q_rank != 3 && q_rank != 4) { + OPENVINO_MLIR_DEBUG_PRINT("SDPAPattern: rejected " << node->get_friendly_name() + << " — unsupported rank " << q_rank << " (expected 3 or 4)"); return false; } // Causal attention is not supported if (node->get_causal()) { + OPENVINO_MLIR_DEBUG_PRINT("SDPAPattern: rejected " << node->get_friendly_name() + << " — causal attention"); return false; } const auto input_size = node->get_input_size(); // Sink parameter (6th input) is not supported if (input_size >= 6) { + OPENVINO_MLIR_DEBUG_PRINT("SDPAPattern: rejected " << node->get_friendly_name() + << " — sink parameter (6th input) is not supported"); return false; } @@ -155,6 +167,8 @@ SDPAPattern::SDPAPattern() const auto mask_shape = node->get_input_partial_shape(3); const bool has_mask = mask_shape.rank().is_dynamic() || mask_shape.rank().get_length() > 0; if (has_mask && mask_shape.is_dynamic()) { + OPENVINO_MLIR_DEBUG_PRINT("SDPAPattern: rejected " << node->get_friendly_name() + << " — dynamic mask shape"); return false; } } @@ -162,6 +176,8 @@ SDPAPattern::SDPAPattern() // Scale (input 4) must be a Constant, dynamic scale input is not supported if (input_size > 4 && !std::dynamic_pointer_cast(node->get_input_node_shared_ptr(4))) { + OPENVINO_MLIR_DEBUG_PRINT("SDPAPattern: rejected " << node->get_friendly_name() + << " — non-constant scale input"); return false; } @@ -172,8 +188,48 @@ SDPAPattern::SDPAPattern() ShapeOfPattern::ShapeOfPattern() : MarkPattern(wrap_type({any_input()}), ConvertShapeOf()) {} +// ConvertSlice only implements the static, all-positive, unit-step case. SlicePattern::SlicePattern() - : MarkPattern(wrap_type({any_input(), any_input(), any_input(), any_input(), any_input()}), ConvertSlice()) {} + : MarkPattern( + wrap_type( + {any_input(), any_input(), any_input(), any_input(), any_input()}, + [](const Output& output) { + auto node = ov::as_type_ptr(output.get_node_shared_ptr()); + if (!node || has_dynamic_rank(node)) { + OPENVINO_MLIR_DEBUG_PRINT("SlicePattern: rejected " << node->get_friendly_name() + << " — dynamic rank"); + return false; + } + + const auto start = ov::as_type_ptr(node->get_input_node_shared_ptr(1)); + const auto stop = ov::as_type_ptr(node->get_input_node_shared_ptr(2)); + const auto step = ov::as_type_ptr(node->get_input_node_shared_ptr(3)); + if (!start || !stop || !step) { + OPENVINO_MLIR_DEBUG_PRINT("SlicePattern: rejected " << node->get_friendly_name() + << " — start/stop/step must be Constants"); + return false; + } + + // Only unit step is supported (sizes = stop - start assumes step == 1). + const auto step_values = step->cast_vector(); + if (std::any_of(step_values.begin(), step_values.end(), [](int64_t s) { return s != 1; })) { + OPENVINO_MLIR_DEBUG_PRINT("SlicePattern: rejected " << node->get_friendly_name() + << " — non-unit step"); + return false; + } + + // Negative start/stop are not handled (no bounds normalization in the converter). + const auto start_values = start->cast_vector(); + const auto stop_values = stop->cast_vector(); + if (std::any_of(start_values.begin(), start_values.end(), [](int64_t v) { return v < 0; }) || + std::any_of(stop_values.begin(), stop_values.end(), [](int64_t v) { return v < 0; })) { + OPENVINO_MLIR_DEBUG_PRINT("SlicePattern: rejected " << node->get_friendly_name() + << " — negative start/stop"); + return false; + } + return true; + }), + ConvertSlice()) {} SqueezePattern::SqueezePattern() : MarkPattern(wrap_type({any_input()}), ConvertSqueeze()) {} From e4b3cc507dd036fdedc2eb70b7dba22177ec549e Mon Sep 17 00:00:00 2001 From: dchigarev Date: Wed, 5 Aug 2026 12:45:05 +0000 Subject: [PATCH 112/121] Fix 'rank' packing into 'void*' vector Signed-off-by: dchigarev --- .../intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp | 2 +- src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp index 5e83be29bd48e4..10bb27bad591dc 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp @@ -106,7 +106,7 @@ bool MLIREvaluateGcGPU::invoke_packed(std::vector& args, const ov::Evalua for (size_t i = 0; i < args.size(); i += kStride) { exec.arg( /*alignedPtr=*/args[i], - /*rank=*/reinterpret_cast(args[i + 1]), + /*rank=*/static_cast(reinterpret_cast(args[i + 1])), /*shape=*/reinterpret_cast(args[i + 2]), /*strides=*/reinterpret_cast(args[i + 3]), /*isUsm=*/reinterpret_cast(args[i + 4]) != 0 diff --git a/src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp index 514a8f28dea379..9cd1dfc9c0cc9b 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp @@ -61,7 +61,7 @@ struct MemRefDescriptor { void append_to_packed_args(std::vector& args, bool is_usm) { args.push_back(aligned); - args.push_back(reinterpret_cast(shape.size())); + args.push_back(reinterpret_cast(static_cast(shape.size()))); args.push_back(shape.data()); args.push_back(strides.data()); args.push_back(reinterpret_cast(static_cast(is_usm))); From b94dc3f26c9a31b21ddb1b283a00773d656decff Mon Sep 17 00:00:00 2001 From: dchigarev Date: Thu, 6 Aug 2026 15:02:45 +0000 Subject: [PATCH 113/121] Fix MemrefDescriptor constructor Signed-off-by: dchigarev --- .../src/plugin/transformations/op/mlir_op.cpp | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp b/src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp index 9cd1dfc9c0cc9b..400650c4468405 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/op/mlir_op.cpp @@ -6,7 +6,9 @@ #include "intel_gpu/op/mlir_op.hpp" +#include #include +#include #include #include @@ -29,16 +31,20 @@ struct MemRefDescriptor { : allocated(tensor.data()), aligned(tensor.data()), offset(0) { - if (module_input_shape.rank() == shape_size(tensor.get_shape())) { - shape.assign(tensor.get_shape().begin(), tensor.get_shape().end()); - } else { - auto it = tensor.get_shape().begin(); - std::advance(it, module_input_shape.rank().get_length()); - shape.assign(tensor.get_shape().begin(), it); - - if (std::any_of(it, tensor.get_shape().end(), [](size_t dim) { return dim != 1; })) { - OPENVINO_THROW("Mismatch in shape sizes"); - } + OPENVINO_ASSERT(module_input_shape.rank().is_static(), "MLIROp: module_input_shape rank must be static"); + const auto module_rank = static_cast(module_input_shape.rank().get_length()); + OPENVINO_ASSERT(module_rank <= tensor.get_shape().size(), + "MLIROp: tensor rank (", + tensor.get_shape().size(), + ") is smaller than expected module rank (", + module_rank, + ")"); + + // Keep only the leading `module_rank` dims; the trailing ones must all be 1 + auto it = std::next(tensor.get_shape().begin(), module_rank); + shape.assign(tensor.get_shape().begin(), it); + if (std::any_of(it, tensor.get_shape().end(), [](size_t dim) { return dim != 1; })) { + OPENVINO_THROW("Mismatch in shape sizes"); } strides.resize(shape.size()); From c565ffff9defb2978f00eef198f23393f71f31c9 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Fri, 7 Aug 2026 15:12:21 +0000 Subject: [PATCH 114/121] Gate unsupported cases for Transpose/Unsqueeze converters Signed-off-by: dchigarev --- .../mlir/conversion/patterns.cpp | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp index 25e755b822fc72..b73a2744e62901 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp @@ -5,6 +5,7 @@ #include "patterns.hpp" #include +#include #include #include @@ -234,11 +235,77 @@ SlicePattern::SlicePattern() SqueezePattern::SqueezePattern() : MarkPattern(wrap_type({any_input()}), ConvertSqueeze()) {} +// ConvertTranspose requires an explicit Constant order covering all input dimensions. TransposePattern::TransposePattern() - : MarkPattern(wrap_type({any_input(), any_input()}), ConvertTranspose()) {} + : MarkPattern( + wrap_type( + {any_input(), any_input()}, + [](const Output& output) { + auto node = ov::as_type_ptr(output.get_node_shared_ptr()); + if (!node || has_dynamic_rank(node)) { + OPENVINO_MLIR_DEBUG_PRINT("TransposePattern: rejected " << output.get_node()->get_friendly_name() + << " — dynamic rank"); + return false; + } + + const auto order = ov::as_type_ptr(node->get_input_node_shared_ptr(1)); + if (!order) { + OPENVINO_MLIR_DEBUG_PRINT("TransposePattern: rejected " << node->get_friendly_name() + << " — non-constant order"); + return false; + } + + // An empty order means "reverse the dimensions" in OpenVINO, but linalg::TransposeOp + // needs an explicit permutation. A non-empty order is already validated to be a + // permutation of the input rank by the Transpose shape inference. + if (order->cast_vector().empty()) { + OPENVINO_MLIR_DEBUG_PRINT("TransposePattern: rejected " << node->get_friendly_name() + << " — empty (implicit reverse) order"); + return false; + } + return true; + }), + ConvertTranspose()) {} +// ConvertUnsqueeze requires Constant axes that are already normalized: non-negative, unique and +// sorted ascending. UnsqueezePattern::UnsqueezePattern() - : MarkPattern(wrap_type({any_input(), any_input()}), ConvertUnsqueeze()) {} + : MarkPattern( + wrap_type( + {any_input(), any_input()}, + [](const Output& output) { + auto node = ov::as_type_ptr(output.get_node_shared_ptr()); + if (!node || has_dynamic_rank(node)) { + OPENVINO_MLIR_DEBUG_PRINT("UnsqueezePattern: rejected " << output.get_node()->get_friendly_name() + << " — dynamic rank"); + return false; + } + + const auto axes = ov::as_type_ptr(node->get_input_node_shared_ptr(1)); + if (!axes) { + OPENVINO_MLIR_DEBUG_PRINT("UnsqueezePattern: rejected " << node->get_friendly_name() + << " — non-constant axes"); + return false; + } + + // The converter reads the axes via Constant::get_coordinate_val(), which requires i64. + if (axes->get_element_type() != element::i64) { + OPENVINO_MLIR_DEBUG_PRINT("UnsqueezePattern: rejected " << node->get_friendly_name() + << " — only i64 axes are supported, got " + << axes->get_element_type()); + return false; + } + + const auto axes_values = axes->cast_vector(); + if (axes_values.empty() || axes_values.front() < 0 || + !std::is_sorted(axes_values.begin(), axes_values.end(), std::less_equal{})) { + OPENVINO_MLIR_DEBUG_PRINT("UnsqueezePattern: rejected " << node->get_friendly_name() + << " — axes must be non-negative, unique and sorted ascending"); + return false; + } + return true; + }), + ConvertUnsqueeze()) {} BinaryEltwisePatternBase::BinaryEltwisePatternBase( NodeTypeInfo wrapped_type, GraphConverter::Convertor convertor, const std::set& element_types) From f55a50dc6452b5751f318fbb1150aed7806795bf Mon Sep 17 00:00:00 2001 From: dchigarev Date: Fri, 7 Aug 2026 17:26:14 +0000 Subject: [PATCH 115/121] Guard out-of-bounds access in ConvertReshape Signed-off-by: dchigarev --- .../transformations/mlir/common/converters/reshape.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp index 53e1c0d2380ea6..09c0481d6e065c 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reshape.hpp @@ -26,21 +26,21 @@ struct ConvertReshape { // Build reassociation by matching accumulated products of src/dst dims. // Each group maps one src dim to multiple dst dims (expand) or vice versa (collapse). SmallVector reassociation; - for (size_t src_i = 0, dst_i = 0; src_i < in_rank && dst_i < out_rank; src_i++, dst_i++) { + for (size_t src_i = 0, dst_i = 0; src_i < in_rank && dst_i < out_rank; ++src_i, ++dst_i) { ReassociationIndices group; int64_t src_prod = in_shape[src_i].get_length(); int64_t dst_prod = out_shape[dst_i].get_length(); if (expand) { // one src dim -> multiple dst dims group.push_back(dst_i); - while (src_prod != dst_prod && dst_i < out_rank) { + while (src_prod != dst_prod && (dst_i + 1) < out_rank) { dst_prod *= out_shape[++dst_i].get_length(); group.push_back(dst_i); } } else { // multiple src dims -> one dst dim group.push_back(src_i); - while (src_prod != dst_prod && src_i < in_rank) { + while (src_prod != dst_prod && (src_i + 1) < in_rank) { src_prod *= in_shape[++src_i].get_length(); group.push_back(src_i); } From a53e9e5946d803f648d357ee660df0910383a8d7 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Mon, 10 Aug 2026 23:01:45 +0000 Subject: [PATCH 116/121] Increased rel_threshold for UnaryElementwiseExpTest Presumably after LLVM comit 55b38aeec8cf, there is a precision degradation. --- .../intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp b/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp index 1084b7cc83a965..2a7554c2f898b7 100644 --- a/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp +++ b/src/plugins/intel_gpu/tests/functional/mlir_op/unary_eltwise.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // +#include + #include "common_test_utils/ov_tensor_utils.hpp" #include "openvino/op/abs.hpp" #include "openvino/op/ceiling.hpp" @@ -36,6 +38,8 @@ class UnaryElementwiseTest : public testing::WithParamInterface) + rel_threshold = 0.011f; const auto& [shape, precision] = GetParam(); auto input = std::make_shared(precision, shape); auto op = std::make_shared(input); From 07761bb547f106765389b799c78b3ea81a94e074 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Tue, 11 Aug 2026 10:36:59 +0000 Subject: [PATCH 117/121] Restrict mlir_primitive serialization Signed-off-by: dchigarev --- .../include/intel_gpu/primitives/mlir_primitive.hpp | 10 ++++++++++ .../src/graph/impls/common/mlir_primitive.cpp | 2 ++ src/plugins/intel_gpu/src/graph/mlir_primitive.cpp | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp b/src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp index 5ce37f9a5037c0..e294e4551a893d 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/primitives/mlir_primitive.hpp @@ -39,6 +39,16 @@ struct mlir_primitive : public primitive_base { std::shared_ptr op; shape_infer_function shape_infer_f; + + void save(BinaryOutputBuffer& /*ob*/) const override { + OPENVINO_THROW("[GPU] Compiled model export / model caching is not supported for models with MLIR " + "(Graph Compiler) subgraphs. Disable GPU_ENABLE_MLIR to use ov::cache_dir or export_model()"); + } + + void load(BinaryInputBuffer& /*ib*/) override { + OPENVINO_THROW("[GPU] Import of a compiled model containing MLIR (Graph Compiler) subgraphs " + "is not supported"); + } }; } // namespace cldnn diff --git a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp index d487da62d98857..a510d209c956b3 100644 --- a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp +++ b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp @@ -41,6 +41,8 @@ struct mlir_primitive_impl : typed_primitive_impl { auto& stream = instance.get_network().get_stream(); const auto& prim = instance.node->get_primitive(); const auto& op = prim->op; + OPENVINO_ASSERT(op, + "[GPU] MLIROp is not set for mlir_primitive '", prim->id); ov::TensorVector input_gpu_tensors; ov::TensorVector output_gpu_tensors; diff --git a/src/plugins/intel_gpu/src/graph/mlir_primitive.cpp b/src/plugins/intel_gpu/src/graph/mlir_primitive.cpp index 40204fe845032e..88b4b0f03ec257 100644 --- a/src/plugins/intel_gpu/src/graph/mlir_primitive.cpp +++ b/src/plugins/intel_gpu/src/graph/mlir_primitive.cpp @@ -31,6 +31,10 @@ std::vector mlir_primitive_inst::calc_output_layouts(mlir_primitive_node input_shapes.push_back(l.get()); } + OPENVINO_ASSERT(prim->shape_infer_f, + "[GPU] Shape inference function is not set for mlir_primitive '", prim->id, + "'. MLIR (Graph Compiler) subgraphs do not support export / model caching"); + std::vector output_shapes = prim->shape_infer_f(input_shapes); std::vector out_layouts; From 1dc594282c397a98a048118e8a1942f3664c183c Mon Sep 17 00:00:00 2001 From: dchigarev Date: Tue, 11 Aug 2026 13:18:09 +0000 Subject: [PATCH 118/121] fix clang-tidy Signed-off-by: dchigarev --- .../intel_gpu/src/graph/impls/common/mlir_primitive.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp index a510d209c956b3..fe612bf7eba996 100644 --- a/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp +++ b/src/plugins/intel_gpu/src/graph/impls/common/mlir_primitive.cpp @@ -65,7 +65,7 @@ struct mlir_primitive_impl : typed_primitive_impl { case allocation_type::usm_host: case allocation_type::usm_shared: case allocation_type::usm_device: { - auto usm_ptr = mem->buffer_ptr(); + auto* usm_ptr = mem->buffer_ptr(); // Seems to only occur with Out-Of-Order queues sometimes. Can't reproduce this anymore, uncomment if needed. // HACK: force move to device, can we do better than this? // auto gpu_buff = dynamic_cast(mem.get()); @@ -111,7 +111,7 @@ struct mlir_primitive_impl : typed_primitive_impl { if (stream.get_queue_type() == QueueTypes::out_of_order) { std::vector depends; depends.reserve(dependent_events.size()); - for (auto& ev : dependent_events) { + for (const auto& ev : dependent_events) { if (!ev) { continue; } @@ -139,7 +139,7 @@ struct mlir_primitive_impl : typed_primitive_impl { if (!result_events.empty()) { std::vector events; events.reserve(result_events.size()); - for (auto event : result_events) { + for (auto* event : result_events) { events.push_back(stream.create_base_event(event)); } return stream.aggregate_events(events, true); From 1c9c6ebfb08addc2ed4c7fe4113b0caca4313f71 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Tue, 11 Aug 2026 14:20:09 +0000 Subject: [PATCH 119/121] add notes regarding integer support Signed-off-by: dchigarev --- .../plugin/transformations/mlir/common/convert_common.cpp | 7 +++++++ .../transformations/mlir/common/converters/gather.hpp | 1 + .../transformations/mlir/common/converters/reduce.hpp | 4 ++++ .../plugin/transformations/mlir/common/converters/relu.hpp | 1 + .../plugin/transformations/mlir/conversion/patterns.cpp | 1 + 5 files changed, 14 insertions(+) diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp index 2c6e10547a8c33..b6e50915577e4b 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/convert_common.cpp @@ -43,6 +43,13 @@ Type importPrecision(MLIRContext* ctx, const ov::element::Type& precision) { return Float16Type::get(ctx); case ov::element::Type_t::bf16: return BFloat16Type::get(ctx); + // FIXME: Distinguishing between signed and unsigned integers (and integer + // handling overall) is not properly supported by the MLIR path at the moment: + // sign-factor is lost here and everything downstream treats the value as signed + // (linalg named ops build signed arith payloads, e.g. linalg.div -> arith.divsi). + // Actual data computation is only tested on float types; the integer types are kept + // mainly for 'technical' values like axes, shapes and indices. Unsigned values that do not + // fit into the signed range of the same bit width may silently produce incorrect results. case ov::element::Type_t::i64: case ov::element::Type_t::u64: return IntegerType::get(ctx, 64); diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp index b85f29f94d2c6f..96d051ce937f1d 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/gather.hpp @@ -16,6 +16,7 @@ namespace ov::intel_gpu::mlir { +// TODO: add signed/unsigned integers support struct ConvertGather { Operation* operator()(ConversionContext& context, NodePtr node) { // TODO: support batch attribute diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp index 4eec227e357a7b..802a11165dd011 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/reduce.hpp @@ -110,6 +110,8 @@ struct ConvertReduce { } private: + // FIXME: mlir::Type does not carry the 'isUnsigned' information at this level, so we have + // to use the ov::element::Type instead (TODO: add tests for the integer case). Value create_init_value(::mlir::OpBuilder& builder, ::mlir::Location loc, ::mlir::Type type) { if constexpr (std::is_same_v) { if (type.isFloat()) { @@ -136,6 +138,8 @@ struct ConvertReduce { } } + // FIXME: mlir::Type does not carry the 'isUnsigned' information at this level, so we have + // to use the ov::element::Type instead (TODO: add tests for the integer case). Value create_payload_op(::mlir::OpBuilder& builder, ::mlir::Location loc, Value lhs, Value rhs, ::mlir::Type type) { if constexpr (std::is_same_v) { if (type.isFloat()) { diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp index a94e3f2cf23133..9ecc7793ba5f0c 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/common/converters/relu.hpp @@ -14,6 +14,7 @@ namespace ov::intel_gpu::mlir { +// TODO: add signed/unsigned integers support struct ConvertRelu { Operation* operator()(ConversionContext& context, NodePtr node) { auto loc = createLocation(context.context, node); diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp index b73a2744e62901..f6b0d65036a60a 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/conversion/patterns.cpp @@ -332,6 +332,7 @@ BinaryEltwisePattern::BinaryEltwisePattern(const std::set(), element_types) {} // Explicit template instantiations +// TODO: add signed/unsigned integers support template class BinaryEltwisePattern; template class BinaryEltwisePattern; template class BinaryEltwisePattern; From e03250c79b617756b5a1c2fead5fc16fc34538d7 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Thu, 13 Aug 2026 01:10:43 +0200 Subject: [PATCH 120/121] Fixed errors afetr changes in GC interfaces (#24) * Bump LLVM version to 24 * Fixed errors afetr changes in GC interfaces * Update cmake/llvm.cmake Co-authored-by: Dmitry Chigarev * Removed tests from exclude --------- Co-authored-by: Dmitry Chigarev --- .github/workflows/graph-compiler.yml | 13 ++++++------- cmake/llvm.cmake | 5 ++++- .../plugin/transformations/mlir/mlir_evaluate.cpp | 6 +++--- .../plugin/transformations/mlir/mlir_evaluate.hpp | 4 ++-- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.github/workflows/graph-compiler.yml b/.github/workflows/graph-compiler.yml index 626d3747320732..4beef59ae4c37b 100644 --- a/.github/workflows/graph-compiler.yml +++ b/.github/workflows/graph-compiler.yml @@ -53,14 +53,12 @@ jobs: - name: Test run: | - # These tests require MLIR patches: - # https://github.com/llvm/llvm-project/pull/208932 - # https://github.com/llvm/llvm-project/pull/197281 - exclude='.*ScaledAttnLayerGPUMlirTest.CompareWithRefs.*|mlir_Transpose.*|mlir_ReshapeAndTranspose.*' - export OV_GPU_ENABLE_MLIR=1 export OV_GPU_QUEUE_TYPE=out-of-order func_tests="$OUTPUT_DIR/bin/intel64/Release/ov_gpu_func_tests" + + # Regex patterns of tests to exclude from the run. + exclude='' filter=$(printf '%s:' \ 'MLIRExecution.SimpleMatmulf16' \ 'MLIRExecution.SDPABasic' \ @@ -68,8 +66,9 @@ jobs: 'mlir_*' \ ) tests=$("$func_tests" --gtest_list_tests --gtest_filter="${filter%:}" \ - | awk -v exclude="$exclude" '/^ /{print suite $1} /^[^ ]/{suite=$1}' \ - | grep -v -E "$exclude") + | awk -v exclude="$exclude" '/^ /{print suite $1} /^[^ ]/{suite=$1}') + [ -n "$exclude" ] && tests=$(echo "$tests" | grep -v -E "$exclude") + start=$SECONDS echo "$tests" | xargs -P 32 -I{} "$func_tests" --gtest_filter='{}' echo "Run $(echo "$tests" | wc -l) tests in $((SECONDS - start))s" diff --git a/cmake/llvm.cmake b/cmake/llvm.cmake index d93992ca6256f8..909c95351d1eba 100644 --- a/cmake/llvm.cmake +++ b/cmake/llvm.cmake @@ -1,6 +1,6 @@ include_guard() -set(SUPPORTED_LLVM_VERSION "23" CACHE STRING "") +set(SUPPORTED_LLVM_VERSION "24" CACHE STRING "") find_package(LLVM CONFIG QUIET) if (NOT LLVM_FOUND) @@ -15,3 +15,6 @@ if (NOT MLIR_FOUND) set(MLIR_DIR "${llvm_cmake_dir}/mlir" CACHE PATH "" FORCE) find_package(MLIR REQUIRED CONFIG) endif() + +message(STATUS "LLVM_DIR: ${LLVM_DIR}") +message(STATUS "MLIR_DIR: ${MLIR_DIR}") diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp index 10bb27bad591dc..b8ef9ad7771de0 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.cpp @@ -60,7 +60,7 @@ MLIREvaluateGcGPU::MLIREvaluateGcGPU(OwningOpRef<::mlir::ModuleOp> _module, auto device = extract_device_from_context(context); if (auto mod = builder.build(device, context)) { - module = *mod; + module = std::make_unique(*mod); } else { OPENVINO_THROW("Failed to build gc::gpuOclModule module"); } @@ -71,7 +71,7 @@ bool MLIREvaluateGcGPU::invoke(const ov::TensorVector& inputs, const ov::EvaluationContext& evaluationContext) { std::vector waitList; gc::gpu::OclContext ctx = build_ocl_context(evaluationContext, waitList); - gc::gpu::StaticExecutor exec(module); + gc::gpu::StaticExecutor<> exec(*module); auto it = evaluationContext.find(ov::internal::mlir_meta::is_kernel_arg_usm.name()); if (it == evaluationContext.end()) { @@ -95,7 +95,7 @@ bool MLIREvaluateGcGPU::invoke(const ov::TensorVector& inputs, bool MLIREvaluateGcGPU::invoke_packed(std::vector& args, const ov::EvaluationContext& evaluationContext) { std::vector waitList; gc::gpu::OclContext ctx = build_ocl_context(evaluationContext, waitList); - gc::gpu::DynamicExecutor exec(module); + gc::gpu::DynamicExecutor<> exec(*module); // Layout (5 pointers per memref, see MemRefDescriptor::append_to_packed_args // in transformations/op/mlir_op.cpp): diff --git a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.hpp b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.hpp index 9222962bda7d70..478e0a0bee783c 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.hpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/mlir/mlir_evaluate.hpp @@ -22,13 +22,13 @@ using ::mlir::ModuleOp; using ::mlir::OwningOpRef; class MLIREvaluateGcGPU : public MLIREvaluateBase { - std::shared_ptr module; + std::unique_ptr module; public: MLIREvaluateGcGPU(OwningOpRef _module, std::shared_ptr loweringContext); - bool requires_packed_args() const override { return !module->isStatic; } + bool requires_packed_args() const override { return !module->isStatic(); } bool invoke(const ov::TensorVector& inputs, ov::TensorVector& outputs, const ov::EvaluationContext& evaluationContext) override; From cbf44299947f74e398da7e2186ad884a21340223 Mon Sep 17 00:00:00 2001 From: dchigarev Date: Thu, 13 Aug 2026 06:56:23 +0000 Subject: [PATCH 121/121] Update gc-pin to 'ov_pin/0.1.1' Signed-off-by: dchigarev --- .github/workflows/dev_gpu_linux_mlir.yml | 2 +- cmake/graph-compiler.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev_gpu_linux_mlir.yml b/.github/workflows/dev_gpu_linux_mlir.yml index e36b33369a3ebc..ea838a648032e3 100644 --- a/.github/workflows/dev_gpu_linux_mlir.yml +++ b/.github/workflows/dev_gpu_linux_mlir.yml @@ -75,7 +75,7 @@ permissions: read-all env: GRAPH_COMPILER_URL: 'https://github.com/dchigarev/graph-compiler.git' - GRAPH_COMPILER_REF: ${{ inputs.graph-compiler-ref || 'ov_pin/0.1.0' }} + GRAPH_COMPILER_REF: ${{ inputs.graph-compiler-ref || 'ov_pin/0.1.1' }} jobs: Smart_CI: diff --git a/cmake/graph-compiler.cmake b/cmake/graph-compiler.cmake index 6487f573a44869..46b51350f68ba0 100644 --- a/cmake/graph-compiler.cmake +++ b/cmake/graph-compiler.cmake @@ -6,7 +6,7 @@ find_package(GraphCompiler QUIET CONFIG) if (NOT GraphCompiler_FOUND) option(GRAPH_COMPILER_DYLINK "Use dynamic linking with GraphCompiler" OFF) set(GRAPH_COMPILER_REPO "https://github.com/dchigarev/graph-compiler" CACHE STRING "GraphCompiler repository URL") - set(GRAPH_COMPILER_TAG "ov_pin/0.1.0" CACHE STRING "GraphCompiler git tag/branch") + set(GRAPH_COMPILER_TAG "ov_pin/0.1.1" CACHE STRING "GraphCompiler git tag/branch") message(STATUS "GraphCompiler not found, fetching from: ${GRAPH_COMPILER_REPO}") include(FetchContent) FetchContent_Declare(