From 847485f001dec9015ec0c10503724a7213fd05c8 Mon Sep 17 00:00:00 2001 From: SaeHie Park Date: Mon, 10 Feb 2025 18:24:55 +0900 Subject: [PATCH] DRAFT initial circle-mlir project on-going draft to introduce initial circle-mlir project. Signed-off-by: SaeHie Park --- .github/workflows/run-circle-mlir-build.yml | 13 +- circle-mlir/CMakeLists.txt | 5 + circle-mlir/Makefile.aa | 22 +++ circle-mlir/Makefile.sample | 86 ++++++++- circle-mlir/README.md | 172 ++++++++++++++++++ circle-mlir/circle-mlir/CMakeLists.txt | 3 + circle-mlir/circle-mlir/lib/CMakeLists.txt | 2 + .../circle-mlir/lib/dialect/CMakeLists.txt | 1 + .../circle-mlir/lib/dialect/mlir/CircleOps.td | 38 ++++ .../lib/dialect/src/CircleDialect.cpp | 2 +- .../lib/dialect/src/ShapeInference.cpp | 17 +- .../circle-mlir/lib/dialect/src/ops/AddOp.h | 66 +++++++ .../circle-mlir/tools-test/CMakeLists.txt | 2 + .../tools-test/gen-onnx/CMakeLists.txt | 85 +++++++++ .../tools-test/gen-onnx/run_gen_onnx.py | 64 +++++++ .../tools-test/gen-onnx/run_gen_onnx.sh | 48 +++++ circle-mlir/circle-mlir/tools/CMakeLists.txt | 0 circle-mlir/externals/CMakeLists.txt | 59 +++++- circle-mlir/infra/cmake/UseAbseil.cmake | 2 +- circle-mlir/infra/cmake/UseFlatbuffers.cmake | 2 +- circle-mlir/infra/cmake/UseMLIR.cmake | 12 +- .../models/unit/Add_F32_R4/__init__.py | 19 ++ .../models/unit/Add_F32_R4_C1/__init__.py | 22 +++ 23 files changed, 724 insertions(+), 18 deletions(-) create mode 100644 circle-mlir/Makefile.aa create mode 100644 circle-mlir/circle-mlir/lib/dialect/src/ops/AddOp.h create mode 100644 circle-mlir/circle-mlir/tools-test/gen-onnx/CMakeLists.txt create mode 100644 circle-mlir/circle-mlir/tools-test/gen-onnx/run_gen_onnx.py create mode 100644 circle-mlir/circle-mlir/tools-test/gen-onnx/run_gen_onnx.sh create mode 100644 circle-mlir/circle-mlir/tools/CMakeLists.txt create mode 100644 circle-mlir/models/unit/Add_F32_R4/__init__.py create mode 100644 circle-mlir/models/unit/Add_F32_R4_C1/__init__.py diff --git a/.github/workflows/run-circle-mlir-build.yml b/.github/workflows/run-circle-mlir-build.yml index fdeb27709d7..5585a895ede 100644 --- a/.github/workflows/run-circle-mlir-build.yml +++ b/.github/workflows/run-circle-mlir-build.yml @@ -35,6 +35,7 @@ jobs: include: - ubuntu_code: jammy ubuntu_vstr: u2204 + one_comp_ver: 1.29.0 runs-on: ubuntu-latest @@ -45,11 +46,19 @@ jobs: name: circle-mlir ${{ matrix.ubuntu_vstr }} ${{ matrix.type }} test steps: + # TODO prepare circle-interpreter Debian package and install + - name: Install one-compiler + run: | + cd /var/tmp + ONE_COMPILER=one-compiler-${{ matrix.ubuntu_code }}_${{ matrix.one_comp_ver }}_amd64.deb + wget https://github.com/Samsung/ONE/releases/download/${{ matrix.one_comp_ver }}/${ONE_COMPILER} + ls -al . + dpkg -i ${ONE_COMPILER} + ls -al /usr/share/one/bin + - name: Checkout uses: actions/checkout@v4 - # TODO download circle-interpreter - # NOTE Docker image has pre-installed submodules in /workdir # NOTE Docker image has pre-installed python packages - name: Configure diff --git a/circle-mlir/CMakeLists.txt b/circle-mlir/CMakeLists.txt index 5fa159eaa61..6ce1b9e9e05 100644 --- a/circle-mlir/CMakeLists.txt +++ b/circle-mlir/CMakeLists.txt @@ -43,5 +43,10 @@ if(CIRCLE_MLIR_LOCALINST) endif() set(RES_CIRCLE_SCHEMA "${CMAKE_SOURCE_DIR}/../res/CircleSchema") +if(NOT CIRCLE_MLIR_EXTERNALS) + set(EXTERNALS_BIN_DIR "${CMAKE_BINARY_DIR}/externals") +else() + set(EXTERNALS_BIN_DIR "${CMAKE_SOURCE_DIR}/${CIRCLE_MLIR_EXTERNALS}") +endif() add_subdirectory(circle-mlir) diff --git a/circle-mlir/Makefile.aa b/circle-mlir/Makefile.aa new file mode 100644 index 00000000000..f1768e015b9 --- /dev/null +++ b/circle-mlir/Makefile.aa @@ -0,0 +1,22 @@ +all: cfg debug test install + +cfg: + Python3_ROOT_DIR=/usr/bin cmake -B build/debug -S ./ \ + -DCMAKE_INSTALL_PREFIX=build/debug.install \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCIRCLE_MLIR_WORKDIR=/workdir + +cc: + Python3_ROOT_DIR=/usr/bin cmake -B build/debug -S ./ \ + -DCMAKE_INSTALL_PREFIX=build/debug.install \ + -DCMAKE_BUILD_TYPE=Debug + +debug: + cmake --build build/debug -j4 + +test: + CTEST_OUTPUT_ON_FAILURE=1 cmake --build build/debug --verbose -- test + +install: + cmake --build build/debug -j4 -- install + diff --git a/circle-mlir/Makefile.sample b/circle-mlir/Makefile.sample index f2ac6f81617..0c77cc7b856 100644 --- a/circle-mlir/Makefile.sample +++ b/circle-mlir/Makefile.sample @@ -15,8 +15,14 @@ endif # TODO error handle if not found PYTHON3_PATH=$(shell dirname $(PYTHON3_CMD)) +# NOTE CIRCLEM_LIR_XXX is used for CMakeLists +# CIRCLEMLIR_XXX is used in this Makefile + CIRCLEMLIR_BUILD_DEBUG?=build/debug CIRCLEMLIR_BUILD_REL?=build/release +CIRCLEMLIR_BUILD_COV?=build/coverage +CIRCLEMLIR_EXTS_DEBUG?=build/externals/debug +CIRCLEMLIR_EXTS_REL?=build/externals/release CIRCLEMLIR_PY3_ROOT?=$(PYTHON3_PATH) CIRCLEMLIR_BUILD_JOBS?=4 @@ -38,7 +44,16 @@ help: @echo "make prepr : prepare externals for release (needed only once)" @echo "make cfgr : configure circle-mlir for release build" @echo "make rel : build for release" + @echo "make prepcov : prepare submodules for coverage test (needed only once)" + @echo "make cfgcov : configure circle-mlir for debug build with coverage test" + @echo "make debugcov : build for test coverage" + @echo "make testcov : run coverage test" + @echo "make gencov : generate test coverage report" + @echo "make cleancov : clean test coverage build" @echo "make testr : test for release" + @echo "make cfgdi : configure circle-mlir for debug build in Docker image" + @echo "make cfgcovdi : configure circle-mlir for debug build with coverage test in Docker image" + @echo "make cfgri : configure circle-mlir for release build in Docker image" @echo "make cleanr : clean release build" @echo "make cleanall : clean all build including overlay, externals" @@ -64,11 +79,12 @@ overlay: prep: _mkbuild Python3_ROOT_DIR=$(CIRCLEMLIR_PY3_ROOT) \ - cmake -B $(CIRCLEMLIR_BUILD_DEBUG)/externals -S ./externals -DCMAKE_BUILD_TYPE=Debug - cmake --build $(CIRCLEMLIR_BUILD_DEBUG)/externals -j$(CIRCLEMLIR_BUILD_JOBS) + cmake -B $(CIRCLEMLIR_EXTS_DEBUG) -S ./externals -DCMAKE_BUILD_TYPE=Debug + cmake --build $(CIRCLEMLIR_EXTS_DEBUG) -j$(CIRCLEMLIR_BUILD_JOBS) cfg: _mkbuild cmake -B $(CIRCLEMLIR_BUILD_DEBUG) -S ./ \ + -DCIRCLE_MLIR_EXTERNALS=$(CIRCLEMLIR_EXTS_DEBUG) \ -DONNX2CIRCLE_TEST_MODELS_SINGLE=ON debug: @@ -83,16 +99,49 @@ clean: rm -f $(CIRCLEMLIR_BUILD_DEBUG)/CMakeCache.txt rm -rf $(CIRCLEMLIR_BUILD_DEBUG)/circle-mlir/ +#------------------------------------------------------------------------------- +# for debug test coverage + +prepcov: _mkbuildcov + Python3_ROOT_DIR=$(CIRCLEMLIR_PY3_ROOT) \ + cmake -B $(CIRCLEMLIR_EXTS_DEBUG) -S ./externals -DCMAKE_BUILD_TYPE=Release + cmake --build $(CIRCLEMLIR_EXTS_DEBUG) -j$(CIRCLEMLIR_BUILD_JOBS) + +cfgcov: _mkbuildcov + cmake -B $(CIRCLEMLIR_BUILD_COV) -S ./ \ + -DCIRCLE_MLIR_EXTERNALS=$(CIRCLEMLIR_EXTS_DEBUG) \ + -DONNX2CIRCLE_TEST_MODELS_SINGLE=ON \ + -DENABLE_COVERAGE=ON + +debugcov: + CM_PASS_DUMP=2 \ + cmake --build $(CIRCLEMLIR_BUILD_COV) -j$(CIRCLEMLIR_BUILD_JOBS) + +# NOTE to configure in Docker, use "make cfgcovdi" + +testcov: + CM_PASS_DUMP=2 \ + CTEST_OUTPUT_ON_FAILURE=1 \ + cmake --build $(CIRCLEMLIR_BUILD_COV) --verbose -- test + +gencov: + bash infra/tools/gen-coverage-report circle-mlir + +cleancov: + rm -f $(CIRCLEMLIR_BUILD_COV)/CMakeCache.txt + rm -rf $(CIRCLEMLIR_BUILD_COV)/circle-mlir/ + #------------------------------------------------------------------------------- # for release prepr: _mkbuildr Python3_ROOT_DIR=$(CIRCLEMLIR_PY3_ROOT) \ - cmake -B $(CIRCLEMLIR_BUILD_REL)/externals -S ./externals -DCMAKE_BUILD_TYPE=Release - cmake --build $(CIRCLEMLIR_BUILD_REL)/externals -j$(CIRCLEMLIR_BUILD_JOBS) + cmake -B $(CIRCLEMLIR_EXTS_REL) -S ./externals -DCMAKE_BUILD_TYPE=Release + cmake --build $(CIRCLEMLIR_EXTS_REL) -j$(CIRCLEMLIR_BUILD_JOBS) cfgr: _mkbuildr - cmake -B $(CIRCLEMLIR_BUILD_REL) -S ./ -DCMAKE_BUILD_TYPE=Release + cmake -B $(CIRCLEMLIR_BUILD_REL) -S ./ -DCMAKE_BUILD_TYPE=Release \ + -DCIRCLE_MLIR_EXTERNALS=$(CIRCLEMLIR_EXTS_REL) rel: cmake --build $(CIRCLEMLIR_BUILD_REL) -j$(CIRCLEMLIR_BUILD_JOBS) @@ -105,6 +154,33 @@ cleanr: rm -f $(CIRCLEMLIR_BUILD_REL)/CMakeCache.txt rm -rf $(CIRCLEMLIR_BUILD_REL)/circle-mlir/ +#------------------------------------------------------------------------------- +# for debug build in Docker +# +# no need to make for overlay, prep as prepared in Docker image +# run make for 'cfgdi' +# then make for 'debug', 'test' + +cfgdi: _mkbuild + cmake -B $(CIRCLEMLIR_BUILD_DEBUG) -S ./ \ + -DONNX2CIRCLE_TEST_MODELS_SINGLE=ON \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCIRCLE_MLIR_WORKDIR=/workdir + +# for test converage build in Docker +cfgcovdi: _mkbuildcov + cmake -B $(CIRCLEMLIR_BUILD_COV) -S ./ \ + -DONNX2CIRCLE_TEST_MODELS_SINGLE=ON \ + -DCIRCLE_MLIR_WORKDIR=/workdir \ + -DENABLE_COVERAGE=ON + +# for release build in Docker + +cfgri: _mkbuild + cmake -B $(CIRCLEMLIR_BUILD_REL) -S ./ \ + -DONNX2CIRCLE_TEST_MODELS_SINGLE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DCIRCLE_MLIR_WORKDIR=/workdir #------------------------------------------------------------------------------- diff --git a/circle-mlir/README.md b/circle-mlir/README.md index 6fd75e5dda5..e5671fa04ac 100644 --- a/circle-mlir/README.md +++ b/circle-mlir/README.md @@ -1,3 +1,175 @@ # circle-mlir Circle MLIR dialect and tools + +## Tools provided + +_onnx2circle_ +- conversion tool of ONNX to Circle model for `compiler` +- to replace not-maintained-anymore onnx-tensorflow package + +## How to build + +Use provided `Makefile.sample` or create your own `Makefile` +``` +ln -s Makefile.sample Makefile +``` +- `Makefile` is in `.gitignore` to let developers use own Makefile. + +### Prerequisite + +``` +sudo apt-get install build-essential cmake git fakeroot +sudo apt-get install autoconf automake libtool unzip wget +sudo apt-get install devscripts debmake debhelper lcov +sudo apt-get install python3 python3-pip python3-venv python3-dev python3-all dh-python + +python3 -m pip install --upgrade pip setuptools +python3 -m pip install yapf==0.43.0 numpy==1.26.4 h5py==3.8.0 einops +``` + +### Prepare externals + +### Debug build + +Prepare overlay +``` +make overlay +``` + +Build submodules in venv +``` +source infra/overlay/venv/bin/activate +make prep +``` +NOTE `llvm-project` is built as `Debug` which may require 32G or more RAM. +- if build fails for some reason, please change back to + `-DCMAKE_BUILD_TYPE=Release` in `prep:` target in `Makefile.sample` file. +- build and test needs venv python packages. + +NOTE `overlay` and `submodules` builds are needed only once. + +Configure and build +``` +make cfg +make debug +``` + +Test build +``` +make test +``` +- optionally, set `ONE_COMPILER_ROOT` to alternate PATH for local ONE build + ``` + ONE_COMPILER_ROOT=/home/user/one/build/install make test + ``` + +To clean up existing build results +``` +make clean +``` + +To clean up also `overlay` and `submodules` +``` +make cleanall +``` +- NOTE when using `CIRCLE_MLIR_LOCALINST`, need to manually clean up this folder + +### Release build + +Release build is available as follows. +Others not mentioned are same as above Debug build. + +Build submodules in venv +``` +source infra/overlay/venv/bin/activate +make prepr +deactivate +``` + +Configure and build +``` +make cfgr +make rel +``` + +Test build +``` +make testr +``` + +### Test coverage + +To get test coverage report, run as following commands. +- assume you already have done `make overlay` and `make prepcov` +- you can skip `make prepcov` step if you are using local installation with `CIRCLE_MLIR_LOCALINST` +- or you can reuse `CIRCLE_MLIR_LOCALINST` for existing debug or release build submodules with +`cfgcov` target such as `CIRCLE_MLIR_LOCALINST=$(pwd)/build/debug/submodules make cfgcov` +``` +source infra/overlay/venv/bin/activate +make cfgcov +deactivate + +make debugcov +make testcov +make gencov +``` + +Open `converage/html/index.html` file in web browser to see the reports. + +To generate from second run and so on in your local machine, you will have to +remove existing files before running `gencov` +``` +rm -rf coverage +make gencov +``` + +To run this with Docker image, use `cfgcovdi` target instead of `cfgcov`. +``` +make cfgcovdi +make debugcov +make testcov +make gencov +``` + + +## Local format check + +Install prerequiste package. +``` +sudo apt-get install clang-format-12 python3 python3-pip +python3 -m pip install yapf==0.32.0 +``` + +Run format checker. +``` +bash ./infra/tools/format +``` +or with `Makefile` from `Makefile.sample` +``` +make format +``` + +## Dump debug logs + +To see logs during conversion with `onnx2circle` tool, set `CM_PASS_DUMP=1` for +preprocessing ONNX and ONNX to circle conversion, or set `CM_PASS_DUMP=2` to see +additional logs for circle rewrite. + +``` +CM_PASS_DUMP=2 onnx2circle input.onnx output.circle +``` + +You can give `-debug` option to see general MLIR logs or `-debug-only=o2c` +option to see only logs from onnx2circle. + +``` +onnx2circle -debug-only=o2c input.onnx output.circle +``` + +## TensorFlow source code + +Some source codes are referenced from TensorFlow and the file path is added to +inside our source. + +Current codes are from `v2.12.1` tag. diff --git a/circle-mlir/circle-mlir/CMakeLists.txt b/circle-mlir/circle-mlir/CMakeLists.txt index 17da97eaeec..4091ef8746b 100644 --- a/circle-mlir/circle-mlir/CMakeLists.txt +++ b/circle-mlir/circle-mlir/CMakeLists.txt @@ -1,3 +1,6 @@ include(UseMLIR) +include(UseAbseil) + add_subdirectory(lib) +add_subdirectory(tools) add_subdirectory(tools-test) diff --git a/circle-mlir/circle-mlir/lib/CMakeLists.txt b/circle-mlir/circle-mlir/lib/CMakeLists.txt index 23cf52c19e7..bd3b5662271 100644 --- a/circle-mlir/circle-mlir/lib/CMakeLists.txt +++ b/circle-mlir/circle-mlir/lib/CMakeLists.txt @@ -4,4 +4,6 @@ add_subdirectory(arser) add_subdirectory(schema) add_subdirectory(dialect) add_subdirectory(utils) +#add_subdirectory(pass) +#add_subdirectory(import) add_subdirectory(export) diff --git a/circle-mlir/circle-mlir/lib/dialect/CMakeLists.txt b/circle-mlir/circle-mlir/lib/dialect/CMakeLists.txt index a5c3ae90acf..c8fcf048469 100644 --- a/circle-mlir/circle-mlir/lib/dialect/CMakeLists.txt +++ b/circle-mlir/circle-mlir/lib/dialect/CMakeLists.txt @@ -18,6 +18,7 @@ target_include_directories(cirmlir_dialect PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/in # use generated files add_dependencies(cirmlir_dialect circle_mlir_gen_inc) target_include_directories(cirmlir_dialect PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(cirmlir_dialect PUBLIC abseil_cpp) target_link_libraries(cirmlir_dialect PUBLIC circle_schema) target_link_libraries(cirmlir_dialect PUBLIC cirmlir_coverage) diff --git a/circle-mlir/circle-mlir/lib/dialect/mlir/CircleOps.td b/circle-mlir/circle-mlir/lib/dialect/mlir/CircleOps.td index 1c0989feec7..d2f07f977ca 100644 --- a/circle-mlir/circle-mlir/lib/dialect/mlir/CircleOps.td +++ b/circle-mlir/circle-mlir/lib/dialect/mlir/CircleOps.td @@ -296,6 +296,44 @@ class CIR_ConvOp($_op))">>, + ResultsBroadcastableShape, + DeclareOpInterfaceMethods, + Pure, + Commutative, + // TODO enable QuantizableResult, + ]> { + let summary = "Addition operator"; + + let description = [{ + Element-wise addition operation. + }]; + + let arguments = ( + // TODO add more dtypes + ins CIR_TensorOf<[F32, I32, I64]>:$lhs, + CIR_TensorOf<[F32, I32, I64]>:$rhs, + CIR_AFAttr:$fused_activation_function); + + let results = (outs CIR_TensorOf<[F32, I32, I64]>:$output); + + let hasFolder = 1; + + let hasCustomAssemblyFormat = 1; + + let extraClassDefinition = [{ + ParseResult $cppClass::parse(OpAsmParser &parser, OperationState &result) { + return parseOneResultSameOperandTypeOp(parser, result); + } + void $cppClass::print(OpAsmPrinter &p) { + return printOneResultOp(getOperation(), p); + } + }]; + + let hasOptions = 1; +} def CIR_ConstOp : Op bool inferBinShapes(BINOP &op, SmallVector } // namespace -// TODO add AddOp +//===----------------------------------------------------------------------===// +// AddOp +//===----------------------------------------------------------------------===// + +void AddOp::inferShapes() +{ + AddOp op = *this; + SmallVector inferred; + if (!inferBinShapes(op, inferred)) + return; + + auto input0_op = getOperand(0); + auto input0_type = input0_op.getType().cast(); + RankedTensorType inferred_type = RankedTensorType::get(inferred, input0_type.getElementType()); + getResult().setType(inferred_type); +} } // namespace Circle } // namespace mlir diff --git a/circle-mlir/circle-mlir/lib/dialect/src/ops/AddOp.h b/circle-mlir/circle-mlir/lib/dialect/src/ops/AddOp.h new file mode 100644 index 00000000000..89ea40fe1c9 --- /dev/null +++ b/circle-mlir/circle-mlir/lib/dialect/src/ops/AddOp.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// from tensorflow/compiler/mlir/lite/ir/tfl_ops.cc + +#ifndef __CIRCLE_MLIR_DIALECT_OPS_ADD_OP_H__ +#define __CIRCLE_MLIR_DIALECT_OPS_ADD_OP_H__ + +#include "circle-mlir/dialect/CircleDialect.h" + +namespace mlir +{ +namespace Circle +{ + +// Return true if the given Add operation has the CPU kernel supported shapes. +bool VerifyAddOpShapeConstraints(AddOp op) +{ + auto element_type = getElementTypeOrSelf(op.getOutput().getType()); + + // Allows F32 and I32 outputs when the operands have valid shapes, + // which are broadcastable shapes up to four dimensions or have same shapes. + // TODO support Quantized Type + if (element_type.isF32() || IsI32Type(element_type) || IsI64Type(element_type)) + { + return VerifyOperandsHaveSameShapesOrBroadcastableShape( + /*op=*/op.getOperation(), /*indices=*/ArrayRef{0, 1}, + /*max_bcast_rank=*/4); + } + + return false; +} + +//===----------------------------------------------------------------------===// +// AddOp +//===----------------------------------------------------------------------===// + +OpFoldResult AddOp::fold(FoldAdaptor adaptor) +{ + auto operands = adaptor.getOperands(); + // TODO(b/142478136): Handle fused ops. + if (getFusedActivationFunction() != "NONE") + return {}; + return ConstFoldBinaryOp( + getType(), operands, [](APFloat a, APFloat b) { return a + b; }, + [](APInt a, APInt b) { return a + b; }); +} + +} // namespace Circle +} // namespace mlir + +#endif // __CIRCLE_MLIR_DIALECT_OPS_ADD_OP_H__ diff --git a/circle-mlir/circle-mlir/tools-test/CMakeLists.txt b/circle-mlir/circle-mlir/tools-test/CMakeLists.txt index 814d9bef556..d21e74e6669 100644 --- a/circle-mlir/circle-mlir/tools-test/CMakeLists.txt +++ b/circle-mlir/circle-mlir/tools-test/CMakeLists.txt @@ -1,2 +1,4 @@ # check gtest is OK add_subdirectory(check-gtest) +# generate onnx models +add_subdirectory(gen-onnx) diff --git a/circle-mlir/circle-mlir/tools-test/gen-onnx/CMakeLists.txt b/circle-mlir/circle-mlir/tools-test/gen-onnx/CMakeLists.txt new file mode 100644 index 00000000000..1da9f83322e --- /dev/null +++ b/circle-mlir/circle-mlir/tools-test/gen-onnx/CMakeLists.txt @@ -0,0 +1,85 @@ +# python3 venv folder +# NOTE Docker image for CI doesn't use venv +set(VENV_PATH "${CMAKE_SOURCE_DIR}/infra/overlay/venv") + +# Copy test scripts +unset(GEN_SCRIPT_DEPS ) +macro(COPY_SCRIPT FILENAME) + set(SCRIPT_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}") + set(SCRIPT_DST "${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}") + add_custom_command( + OUTPUT ${SCRIPT_DST} + COMMAND ${CMAKE_COMMAND} -E copy "${SCRIPT_SRC}" "${SCRIPT_DST}" + DEPENDS ${SCRIPT_SRC} + COMMENT "gen-onnx: prepare ${FILENAME}" + ) + list(APPEND GEN_SCRIPT_DEPS "${SCRIPT_DST}") +endmacro(COPY_SCRIPT) + +COPY_SCRIPT(run_gen_onnx.sh) +COPY_SCRIPT(run_gen_onnx.py) + +# Models folder for unit testing +set(PYTORCH_UNIT_PATH "${CMAKE_SOURCE_DIR}/models/unit") +set(PYTORCH_NET_PATH "${CMAKE_SOURCE_DIR}/models/net") + +# Pytorch script files +unset(PYTORCH_UNIT_ITEMS ) +# gather all the names in /models/unit +file(GLOB PYTORCH_MODELS_ITEMS RELATIVE ${PYTORCH_UNIT_PATH} ${PYTORCH_UNIT_PATH}/*) +# for each name, if the item is a folder, add to the list +foreach(PYTORCH_ITEM IN ITEMS ${PYTORCH_MODELS_ITEMS}) + if(IS_DIRECTORY ${PYTORCH_UNIT_PATH}/${PYTORCH_ITEM}) + if(EXISTS ${PYTORCH_UNIT_PATH}/${PYTORCH_ITEM}/__init__.py) + list(APPEND PYTORCH_UNIT_ITEMS ${PYTORCH_ITEM}) + endif() + endif() +endforeach() + +unset(PYTORCH_MODELS_ITEMS) +unset(PYTORCH_NET_ITEMS ) +# gather all the names in /models/net +file(GLOB PYTORCH_MODELS_ITEMS RELATIVE ${PYTORCH_NET_PATH} ${PYTORCH_NET_PATH}/*) +# for each name, if the item is a folder, add to the list +foreach(PYTORCH_ITEM IN ITEMS ${PYTORCH_MODELS_ITEMS}) + if(IS_DIRECTORY ${PYTORCH_NET_PATH}/${PYTORCH_ITEM}) + if(EXISTS ${PYTORCH_NET_PATH}/${PYTORCH_ITEM}/__init__.py) + list(APPEND PYTORCH_NET_ITEMS ${PYTORCH_ITEM}) + endif() + endif() +endforeach() + +unset(GEN_FILES_DEPS) +# for each items in the list, run run_gen_onnx to generate onnx model +foreach(PYTORCH_ITEM IN ITEMS ${PYTORCH_UNIT_ITEMS}) + set(ONNX_FILE ${PYTORCH_ITEM}.onnx) + add_custom_command(OUTPUT ${ONNX_FILE} + COMMAND bash run_gen_onnx.sh + ${VENV_PATH} ${PYTORCH_UNIT_PATH} ${PYTORCH_ITEM} ${ONNX_FILE} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/run_gen_onnx.sh + ${CMAKE_CURRENT_BINARY_DIR}/run_gen_onnx.py + ${PYTORCH_UNIT_PATH}/${PYTORCH_ITEM}/__init__.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generate ${ONNX_FILE}" + ) + list(APPEND GEN_FILES_DEPS ${ONNX_FILE}) +endforeach() + +foreach(PYTORCH_ITEM IN ITEMS ${PYTORCH_NET_ITEMS}) + set(ONNX_FILE ${PYTORCH_ITEM}.onnx) + add_custom_command(OUTPUT ${ONNX_FILE} + COMMAND bash run_gen_onnx.sh + ${VENV_PATH} ${PYTORCH_NET_PATH} ${PYTORCH_ITEM} ${ONNX_FILE} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/run_gen_onnx.sh + ${CMAKE_CURRENT_BINARY_DIR}/run_gen_onnx.py + ${PYTORCH_NET_PATH}/${PYTORCH_ITEM}/__init__.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generate ${ONNX_FILE}" + ) + list(APPEND GEN_FILES_DEPS ${ONNX_FILE}) +endforeach() + +# gen_onnx_target is used to make build dependency +add_custom_target(gen_onnx_target ALL DEPENDS ${GEN_SCRIPT_DEPS} ${GEN_FILES_DEPS}) + +set(GEN_ONNX_PATH ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE) diff --git a/circle-mlir/circle-mlir/tools-test/gen-onnx/run_gen_onnx.py b/circle-mlir/circle-mlir/tools-test/gen-onnx/run_gen_onnx.py new file mode 100644 index 00000000000..ef7e071ce86 --- /dev/null +++ b/circle-mlir/circle-mlir/tools-test/gen-onnx/run_gen_onnx.py @@ -0,0 +1,64 @@ +# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import torch +import importlib +import sys + +from pathlib import Path + + +def generate_onnx(models_root, model_name, onnx_file): + sys.path.append(models_root) + module = importlib.import_module(model_name) + + # default: refer https://github.com/pytorch/pytorch/blob/master/torch/onnx/utils.py + # and https://github.com/pytorch/pytorch/blob/master/torch/onnx/_constants.py + # and https://github.com/pytorch/pytorch/blob/master/tools/onnx/update_default_opset_version.py + opset_version = 14 + if hasattr(module._model_, 'onnx_opset_version'): + opset_version = module._model_.onnx_opset_version() + + m_keys = module.__dict__.keys() + + if '_io_names_' in m_keys and '_dynamic_axes_' in m_keys: + # refer https://github.com/onnx/onnx/issues/654#issuecomment-521233285 + # purpose of this is to set dynamic shape for inputs or inputs + # magic(?) is to set input/output names, and then set dyanmic shape by name/dim + # example) set output dim(0) as unknown + # _io_names_ = [['input'], ['output']] + # _dynamic_axes_ = {'output': {0: '?'}} + torch.onnx.export(module._model_, + module._inputs_, + onnx_file, + input_names=module._io_names_[0], + output_names=module._io_names_[1], + dynamic_axes=module._dynamic_axes_, + opset_version=opset_version) + else: + torch.onnx.export(module._model_, + module._inputs_, + onnx_file, + opset_version=opset_version) + + if hasattr(module._model_, 'post_process'): + module._model_.post_process(onnx_file) + + +if __name__ == "__main__": + if len(sys.argv) != 4: + thispath = Path(sys.argv[0]) + sys.exit("Usage: " + thispath.name + " [models_root] [model_name] [onnx_file]") + + generate_onnx(sys.argv[1], sys.argv[2], sys.argv[3]) diff --git a/circle-mlir/circle-mlir/tools-test/gen-onnx/run_gen_onnx.sh b/circle-mlir/circle-mlir/tools-test/gen-onnx/run_gen_onnx.sh new file mode 100644 index 00000000000..434d45a25b4 --- /dev/null +++ b/circle-mlir/circle-mlir/tools-test/gen-onnx/run_gen_onnx.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# This script executes run_gen_onnx.py file to generate ONNX model +# +# HOW TO USE +# +# ./run_gen_onnx.sh +# venv_dir : python virtual environment home directory +# models : path where python modules exist +# model_name : name of model +# onnx_name : name of onnx file + +THIS_SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PY_SCRIPT_PATH="${THIS_SCRIPT_PATH}/run_gen_onnx.py" + +VENV_PATH="$1"; shift +MODELS_ROOT_PATH="$1"; shift +MODEL_NAME="$1"; shift +ONNX_NAME="$1"; shift + +PASSED_TAG="${ONNX_NAME}.passed" +GENERATE_LOG="${ONNX_NAME}.log" +rm -f "${PASSED_TAG}" + +cat > "${GENERATE_LOG}" <( + exec 2>&1 + set -ex + + # NOTE enter venv if exist + if [[ -f "${VENV_PATH}/bin/activate" ]]; then + source "${VENV_PATH}/bin/activate" + fi + + "python3" "${PY_SCRIPT_PATH}" "${MODELS_ROOT_PATH}" "${MODEL_NAME}" "${ONNX_NAME}" + if [[ $? -eq 0 ]]; then + touch "${PASSED_TAG}" + fi + + if [[ -f "${VENV_PATH}/bin/activate" ]]; then + deactivate + fi +) + +if [[ ! -f "${PASSED_TAG}" ]]; then + exit 255 +fi +rm -f "${PASSED_TAG}" +exit 0 diff --git a/circle-mlir/circle-mlir/tools/CMakeLists.txt b/circle-mlir/circle-mlir/tools/CMakeLists.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/circle-mlir/externals/CMakeLists.txt b/circle-mlir/externals/CMakeLists.txt index d80e50ac14d..ddf08ae6edb 100644 --- a/circle-mlir/externals/CMakeLists.txt +++ b/circle-mlir/externals/CMakeLists.txt @@ -15,9 +15,55 @@ if(NOT CMAKE_BUILD_TYPE) endif(NOT CMAKE_BUILD_TYPE) message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +# NOTE: ld.gold has some problem with pybind11 +option(CIRCLE_MLIR_USE_GOLD "Use ld.gold linker for llvm-project" OFF) +option(CIRCLE_MLIR_USE_CLANG "Use clang and lld for llvm-project and onnx-mlir" OFF) + include(ExternalProject) -set(EXTERNALS_BUILD_INST_DIR ${CMAKE_BINARY_DIR}) +if(DEFINED ENV{CIRCLE_MLIR_LOCALINST}) + set(CIRCLE_MLIR_LOCALINST $ENV{CIRCLE_MLIR_LOCALINST}) +endif() + +if(DEFINED ENV{CIRCLE_MLIR_USE_GOLD}) + set(CIRCLE_MLIR_USE_GOLD ON) +endif() + +if(DEFINED ENV{CIRCLE_MLIR_USE_CLANG}) + set(CIRCLE_MLIR_USE_CLANG ON) +endif() + +if(CIRCLE_MLIR_USE_GOLD AND CIRCLE_MLIR_USE_CLANG) + message(FATAL_ERROR "CIRCLE_MLIR_USE_GOLD and CIRCLE_MLIR_USE_CLANG are exclusive option.") +endif() + +# Use gcc + gold +if(CIRCLE_MLIR_USE_GOLD) + set(CIRCLE_MLIR_GOLD_OPTION -DCMAKE_EXE_LINKER_FLAGS='-Wl,-no-keep-memory,-fuse-ld=gold' -DLLVM_USE_LINKER=gold) + message(STATUS "Use linker ld.gold: ${CIRCLE_MLIR_GOLD_OPTION}") +endif() + +# Use clang + lld +if(CIRCLE_MLIR_USE_CLANG) + find_program(CLANG_COMPILER clang) + if(NOT EXISTS ${CLANG_COMPILER}) + message(FATAL_ERROR "Clang is not available.") + endif() + find_program(LLD_LINKER lld) + if(NOT EXISTS ${LLD_LINKER}) + message(FATAL_ERROR "LLD is not available.") + endif() + set(CIRCLE_MLIR_CLANG_OPTION -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++) + set(CIRCLE_MLIR_CLANG_OPTION ${CIRCLE_MLIR_CLANG_OPTION} -DLLVM_ENABLE_LLD=ON) + message(STATUS "Use Clang and LLD linker: ${CIRCLE_MLIR_CLANG_OPTION}") +endif() + +if(CIRCLE_MLIR_LOCALINST) + message(STATUS "CIRCLE_MLIR_LOCALINST=${CIRCLE_MLIR_LOCALINST}") + set(EXTERNALS_BUILD_INST_DIR ${CIRCLE_MLIR_LOCALINST}) +else() + set(EXTERNALS_BUILD_INST_DIR ${CMAKE_BINARY_DIR}) +endif() set(FB_BUILD_DIR "${EXTERNALS_BUILD_INST_DIR}/flatbuffers-build") set(FB_INSTALL_DIR "${EXTERNALS_BUILD_INST_DIR}/flatbuffers-install") @@ -25,6 +71,10 @@ set(AC_BUILD_DIR "${EXTERNALS_BUILD_INST_DIR}/abseil-cpp-build") set(AC_INSTALL_DIR "${EXTERNALS_BUILD_INST_DIR}/abseil-cpp-install") set(LP_BUILD_DIR "${EXTERNALS_BUILD_INST_DIR}/llvm-project-build") set(LP_INSTALL_DIR "${EXTERNALS_BUILD_INST_DIR}/llvm-project-install") +set(PB_BUILD_DIR "${EXTERNALS_BUILD_INST_DIR}/protobuf-build") +set(PB_INSTALL_DIR "${EXTERNALS_BUILD_INST_DIR}/protobuf-install") +set(OM_BUILD_DIR "${EXTERNALS_BUILD_INST_DIR}/onnx-mlir-build") +set(OM_INSTALL_DIR "${EXTERNALS_BUILD_INST_DIR}/onnx-mlir-install") # build flatbuffers ExternalProject_Add(externals-flatbuffers @@ -62,8 +112,14 @@ ExternalProject_Add(externals-llvm-project INSTALL_DIR "${LP_INSTALL_DIR}" CMAKE_ARGS -DLLVM_ENABLE_PROJECTS=mlir -DLLVM_TARGETS_TO_BUILD=host + -DLLVM_ENABLE_TERMINFO=OFF + -DLLVM_ENABLE_ZLIB=OFF + -DLLVM_ENABLE_ZSTD=OFF + -DLLVM_INCLUDE_TESTS=OFF -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX='${LP_INSTALL_DIR}' + ${CIRCLE_MLIR_GOLD_OPTION} + ${CIRCLE_MLIR_CLANG_OPTION} -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_ENABLE_RTTI=ON # NOTE externals-flatbuffers externals-abseil-cpp are added to DEPENDS only @@ -101,6 +157,7 @@ ExternalProject_Add(externals-onnx-mlir CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH='${CMAKE_PREFIX_PATH}' -DCMAKE_INSTALL_PREFIX='${OM_INSTALL_DIR}' + ${CIRCLE_MLIR_CLANG_OPTION} -DMLIR_DIR='${LP_BUILD_DIR}/lib/cmake/mlir' -DPython3_ROOT_DIR='$ENV{Python3_ROOT_DIR}' -DONNX_MLIR_BUILD_TESTS=OFF diff --git a/circle-mlir/infra/cmake/UseAbseil.cmake b/circle-mlir/infra/cmake/UseAbseil.cmake index 32dd72665e6..31f6df82218 100644 --- a/circle-mlir/infra/cmake/UseAbseil.cmake +++ b/circle-mlir/infra/cmake/UseAbseil.cmake @@ -1,7 +1,7 @@ if(NOT CIRCLE_MLIR_WORKDIR) if(NOT CIRCLE_MLIR_LOCALINST) # for local build - set(ABSEIL_INST_DIR "${CMAKE_BINARY_DIR}/submodules/abseil-cpp-install") + set(ABSEIL_INST_DIR "${EXTERNALS_BIN_DIR}/abseil-cpp-install") else() set(ABSEIL_INST_DIR "${CIRCLE_MLIR_LOCALINST}/abseil-cpp-install") endif() diff --git a/circle-mlir/infra/cmake/UseFlatbuffers.cmake b/circle-mlir/infra/cmake/UseFlatbuffers.cmake index 7639a689138..6699743da11 100644 --- a/circle-mlir/infra/cmake/UseFlatbuffers.cmake +++ b/circle-mlir/infra/cmake/UseFlatbuffers.cmake @@ -1,6 +1,6 @@ if(NOT CIRCLE_MLIR_WORKDIR) if(NOT CIRCLE_MLIR_LOCALINST) - set(FLATBUFFERS_INS "${CMAKE_BINARY_DIR}/submodules/flatbuffers-install") + set(FLATBUFFERS_INS "${EXTERNALS_BIN_DIR}/flatbuffers-install") else() set(FLATBUFFERS_INS "${CIRCLE_MLIR_LOCALINST}/flatbuffers-install") endif() diff --git a/circle-mlir/infra/cmake/UseMLIR.cmake b/circle-mlir/infra/cmake/UseMLIR.cmake index 6830711b66a..af283258f2d 100644 --- a/circle-mlir/infra/cmake/UseMLIR.cmake +++ b/circle-mlir/infra/cmake/UseMLIR.cmake @@ -1,14 +1,14 @@ if(NOT CIRCLE_MLIR_WORKDIR) if(NOT CIRCLE_MLIR_LOCALINST) # for local build - set(LLVM_INST_DIR "${CMAKE_BINARY_DIR}/submodules/llvm-project-install") - set(ONNXMLIR_SRC "${CMAKE_SOURCE_DIR}/submodules/onnx-mlir") - set(ONNXMLIR_BLD "${CMAKE_BINARY_DIR}/submodules/onnx-mlir-build") - set(ONNXMLIR_INS "${CMAKE_BINARY_DIR}/submodules/onnx-mlir-install") - set(PROTOBUF_INS "${CMAKE_BINARY_DIR}/submodules/protobuf-install") + set(LLVM_INST_DIR "${EXTERNALS_BIN_DIR}/llvm-project-install") + set(ONNXMLIR_SRC "${CMAKE_SOURCE_DIR}/externals/onnx-mlir") + set(ONNXMLIR_BLD "${EXTERNALS_BIN_DIR}/onnx-mlir-build") + set(ONNXMLIR_INS "${EXTERNALS_BIN_DIR}/onnx-mlir-install") + set(PROTOBUF_INS "${EXTERNALS_BIN_DIR}//protobuf-install") else() set(LLVM_INST_DIR "${CIRCLE_MLIR_LOCALINST}/llvm-project-install") - set(ONNXMLIR_SRC "${CMAKE_SOURCE_DIR}/submodules/onnx-mlir") + set(ONNXMLIR_SRC "${CMAKE_SOURCE_DIR}/externals/onnx-mlir") set(ONNXMLIR_BLD "${CIRCLE_MLIR_LOCALINST}/onnx-mlir-build") set(ONNXMLIR_INS "${CIRCLE_MLIR_LOCALINST}/onnx-mlir-install") set(PROTOBUF_INS "${CIRCLE_MLIR_LOCALINST}/protobuf-install") diff --git a/circle-mlir/models/unit/Add_F32_R4/__init__.py b/circle-mlir/models/unit/Add_F32_R4/__init__.py new file mode 100644 index 00000000000..f58d7e4ab8f --- /dev/null +++ b/circle-mlir/models/unit/Add_F32_R4/__init__.py @@ -0,0 +1,19 @@ +import torch + + +# Generate Add operator with Float32, Rank-4 +class net_add(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(self, inputs): + return torch.add(inputs[0], inputs[1]) + + def onnx_opset_version(self): + # TODO set version + return 10 + + +_model_ = net_add() + +_inputs_ = [torch.randn(1, 2, 3, 3), torch.randn(1, 2, 3, 3)] diff --git a/circle-mlir/models/unit/Add_F32_R4_C1/__init__.py b/circle-mlir/models/unit/Add_F32_R4_C1/__init__.py new file mode 100644 index 00000000000..55bb946c2ad --- /dev/null +++ b/circle-mlir/models/unit/Add_F32_R4_C1/__init__.py @@ -0,0 +1,22 @@ +import torch +import numpy as np + + +# Generate Add operator with Float32, Rank-4 with Constant input +class net_add(torch.nn.Module): + def __init__(self): + super().__init__() + rng = np.random.default_rng(seed=123) + self.C1 = torch.from_numpy(rng.random((1, 2, 3, 3), dtype=np.float32)) + + def forward(self, inputs): + return torch.add(inputs[0], self.C1) + + def onnx_opset_version(self): + # TODO set version + return 10 + + +_model_ = net_add() + +_inputs_ = [torch.randn(1, 2, 3, 3)]