diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000000..f056a846090 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,68 @@ +# Migrate to clang-format-8 +b823044b5430455dc3d59c465ec36ae2e0a18b06 +1a6d9f00ba9f84c9c0dea1920ab3e4c75c127767 +07e0bf6909d9afdca8eff74dc18824c126e051fe +e01989e10e361b826db5cc7eb367d64bed52c394 +1a6c2f4c728c3fb6af6c925444a414e21b040776 +56713c812379c60d59f1014c495ea450ecb5619b +218e7561a11f5104349b67457915071ce0325844 +41644d4d0c74427112d0df94d627e47d7e1af182 +d1d46e759110533bc406aec983a4f1965783c1bb +13740fae0d2dadfc2e61d8a749eccdda2f59f651 +7484db3e41d443e77bd577189b9b89704b4f77e7 +89294e72fb7f87090cfbc9a64d1efe3127b43707 +e9555b203ba4affd74dadd1766abe2de123759f3 +03b2c33a74858288300c0ea1f8624751eb6cba6b +fbfbad9f72eab095f1ef62f629e0c2004c8ce2d1 +88213431c517cdd01fa38027a4ca59db247349dd +a65367238d22ada6ee67b3bc8c17aafd5ce79fed +07ec6705b265a36bbb110fc92373dfd81dadd977 +b97bb692fb5450b2407f0edaad71de035d2c99df +fa047652d28926c44a167ce3db14b4d699fc4d72 +45f473c88c7a49317cae260afc394335e4d996b0 +987140eb8444b4404035fad2010386e8ebef06d4 +9321f323e685f87c43a958ec18f9c8899e5cec66 +ca07434330c7fb4896731ecd9a6642c4cf3ea32a +4e38da075e0c3049ea2c67e7a23704dbf00976f8 +f3b1e7a0d25da2986368bd9e3c966d20e398cd2f +0bac725cb5fda66712bfe4898f7c2c7339fd9c95 +a7fe0607307cbb7a6c52d6ce88bed61a0546bea9 +21f79d6a95ec9ce1cec1773b994db21936000b84 +e135aa4e84538adeca76eb6c5a3f96b47e14928a +2d3e893883ae1594318980941128cf0dfbfce2f3 +de4fb32af5e79485b52236790149eed44762a0e7 +b27695f7000a84e130b15b872a4c5998a5572449 +45b57db3e26620929d3d653c22702383afc5bd73 +21612ef572e1e6214dece9a7da8b3ed91a35c512 +c32fc746d6ab7a51a7df2f4804e1916f9ea083dd +a6600d1649e6c0789dbd32968f840e08df9609c3 +7a56078687ecd6b6fb5d54cd4fea9f5e32788667 +589bb2dd216592712ed4e1d00fbdde56e0c578df +5365de4f0121efd12eaced56cdb9f3319f403c3e +ce664a17eb02cf34b1291edb9056e831a78abe39 +e3b0dbd1c6166eb89e8f43edb2bc06bc104fe536 +e3b0dbd1c6166eb89e8f43edb2bc06bc104fe536 +950795b83427eb06a381d36ef020093470527763 +ab99392cba9c2aa4d8660e2854143cb3b5696fbd +8ac5bd503ae57152b6b45c80f9c57315461bed9d +536cd365ee933aee0ba9c4c4ed80300170d2b818 +10da7333257bf5b15f007ed03d6dea2b2bc6226e +432a2e2a347c9f824583ef40f7bcb281c2df8ceb +c58f815fd4864b1f9ca03584616b2fae32c262b3 +2b531e4ae4e91a0f202b67a91af0d8de07e72a9c +0da03ab09ea30fcc5d16a3e82d9eb9e353f3f7d6 +f0f13a9add58c17d4cd26aa839b71ab9f1126961 +f40f1757207a4c3fcaeb94cb9c05310d30da45aa +1af2b04dd222a53580ad897d033da02749dea5a3 +559e5a5dee0c5a5d49ae026e8f417e982d3014f1 +06f93e959f17b1e8aae9f66a7d79409961511744 +e45913706aebaaf028045c3efe7a7894f98c30be +151d5c5ec65c8e398a7ff143b770ac7468424082 +070fef8051c1397b76cdcbedc2b4140473f42ddd +241a5b08406f2bed7318455dec8b8aac3541e1f3 + +# Migrate to clang-format-16 +a3a157a3e5a15f2c23ab3c55c0b76aa2db224710 + +# Migrate to yapf 0.40.2 +19e8a47269665cd9d00d27f945d43a6936efdc30 diff --git a/.github/workflows/pub-circle-int-launchpad.yml b/.github/workflows/pub-circle-int-launchpad.yml index 6c39c4bcfa3..6f0621be665 100644 --- a/.github/workflows/pub-circle-int-launchpad.yml +++ b/.github/workflows/pub-circle-int-launchpad.yml @@ -104,7 +104,7 @@ jobs: run: | CIR_INTP_ITEMS="angkor;cwrap;pepper-str;pepper-strcast;pepper-csv2vec;pp" CIR_INTP_ITEMS="${CIR_INTP_ITEMS};oops;loco;logo-core;logo;locop" - CIR_INTP_ITEMS="${CIR_INTP_ITEMS};hermes;hermes-std;safemain;mio-circle08" + CIR_INTP_ITEMS="${CIR_INTP_ITEMS};hermes;hermes-std;safemain;mio-circle" CIR_INTP_ITEMS="${CIR_INTP_ITEMS};luci-compute;luci;luci-interpreter" CIR_INTP_ITEMS="${CIR_INTP_ITEMS};foder;arser;vconone;circle-interpreter" echo ${CIR_INTP_ITEMS} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a6022556834..3db5cde0fac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,10 +10,15 @@ repos: exclude: \.(svg)$ - name: 'Check trailing whitespace' id: trailing-whitespace - # TODO add shebang to all excutable python/shell scripts - name: 'Check executable files' id: check-executables-have-shebangs - exclude: \.(py|sh)$ + exclude: + (?x)^( + .*/__init__.py$| + compiler/nnc/utils/.*\.py$| + runtime/tests/scripts/models/.*/config\.sh$| + res/TensorFlowTests/.*\.py$ + ) - repo: https://github.com/rhysd/actionlint rev: 'v1.7.9' @@ -63,6 +68,16 @@ repos: hooks: - name: 'Check copyright headers' id: copyright-check - entry: 'nnas copyright-check' # Use trick to use nnas command + entry: infra/git-hooks/copyright-check.sh language: script - pass_filenames: false + pass_filenames: true + types: [file] + files: \.(c[cl]?|cpp|h(pp)?)$ + # Ignore 3rd-party code + exclude: + (?x)^( + compiler/ann-api/.*| + onert-micro/externals/.*| + runtime/3rdparty/.*| + runtime/tests/nnapi/.* + ) diff --git a/circle-mlir/circle-mlir/lib/dialect/mlir/CircleOps.td b/circle-mlir/circle-mlir/lib/dialect/mlir/CircleOps.td index 99f30bc7cf0..7096d6abfe8 100644 --- a/circle-mlir/circle-mlir/lib/dialect/mlir/CircleOps.td +++ b/circle-mlir/circle-mlir/lib/dialect/mlir/CircleOps.td @@ -1619,6 +1619,32 @@ def CIR_PReluOp : CIR_Op<"prelu", [ */ } +def CIR_RangeOp : CIR_Op<"range", [ + Pure, + CIR_OperandHasRank<0, 0>, + CIR_OperandHasRank<1, 0>, + CIR_OperandHasRank<2, 0>, + PredOpTrait<"operands and output must have same element type", + And<[TCresVTEtIsSameAsOp<0, 0>, TCresVTEtIsSameAsOp<0, 1>, + TCresVTEtIsSameAsOp<0, 2>]>>]> { + let summary = "Range operator"; + + let description = [{ + Returns a 1D tensor defined by a sequence from `start` to `limit` with + a given `delta`. + }]; + + let arguments = (ins + CIR_TensorOf<[I32, F32, I64]>:$start, + CIR_TensorOf<[I32, F32, I64]>:$limit, + CIR_TensorOf<[I32, F32, I64]>:$delta + ); + + let results = (outs CIR_TensorOf<[I32, F32, I64]>:$result); + + let hasOptions = 1; +} + def CIR_ReduceMaxOp: CIR_Op<"reduce_max", [ PredOpTrait<"input and output must have same element type", CIR_TCresVTEtIsSameAsOp<0, 0>>, @@ -1946,6 +1972,23 @@ def CIR_ShapeOp: CIR_Op<"shape", [ let hasFolder = 1; } +def CIR_SignOp: CIR_Op<"sign", [ + Pure, + SameOperandsAndResultType]> { + let summary = "Sign operation"; + let description = [{ + Returns NaN if x is NaN, 0 if x is 0, -1 if x < 0 and 1 if x > 0. + }]; + + let arguments = (ins + CIR_TensorOf<[F32, F64, I32]>:$x + ); + + let results = (outs + CIR_TensorOf<[F32, F64, I32]>:$output + ); +} + def CIR_SinOp: CIR_Op<"sin", [ Pure, /*TF_SameOperandsAndResultTypeResolveRef*/ diff --git a/circle-mlir/circle-mlir/lib/pass/src/ConvertONNXToCirclePass.cpp b/circle-mlir/circle-mlir/lib/pass/src/ConvertONNXToCirclePass.cpp index 7fa691f0d11..9f5a25812a8 100644 --- a/circle-mlir/circle-mlir/lib/pass/src/ConvertONNXToCirclePass.cpp +++ b/circle-mlir/circle-mlir/lib/pass/src/ConvertONNXToCirclePass.cpp @@ -64,6 +64,7 @@ #include "ops/PowOp.h" #include "ops/PReluOp.h" #include "ops/QuantizeLinearOp.h" +#include "ops/RangeOp.h" #include "ops/ReciprocalOp.h" #include "ops/ReduceMaxOp.h" #include "ops/ReduceMeanOp.h" @@ -75,6 +76,7 @@ #include "ops/ResizeOp.h" #include "ops/ShapeOp.h" #include "ops/SigmoidOp.h" +#include "ops/SignOp.h" #include "ops/SinOp.h" #include "ops/SliceOp.h" #include "ops/SoftmaxOp.h" @@ -247,6 +249,7 @@ void ConvertONNXToCirclePass::runOnOperation() patterns.insert(typeConverter, context); patterns.insert(typeConverter, context); patterns.insert(typeConverter, context); + patterns.insert(typeConverter, context); patterns.insert(typeConverter, context); patterns.insert(typeConverter, context); patterns.insert(typeConverter, context); @@ -263,6 +266,7 @@ void ConvertONNXToCirclePass::runOnOperation() patterns.insert(typeConverter, context); patterns.insert(typeConverter, context); patterns.insert(typeConverter, context); + patterns.insert(typeConverter, context); patterns.insert(typeConverter, context); patterns.insert(typeConverter, context); patterns.insert(typeConverter, context); diff --git a/circle-mlir/circle-mlir/lib/pass/src/ops/ConvOp.h b/circle-mlir/circle-mlir/lib/pass/src/ops/ConvOp.h index d629076d9ff..1e2111179c2 100644 --- a/circle-mlir/circle-mlir/lib/pass/src/ops/ConvOp.h +++ b/circle-mlir/circle-mlir/lib/pass/src/ops/ConvOp.h @@ -94,7 +94,11 @@ class ConvConv : public mlir::OpConversionPattern // for op.pads != [0,0,0,0] std::vector padsValue; if (GetPads(op.getPads(), padsValue)) + { inputPreTr = insertPad(rewriter, op_name, input, outtype, padsValue); + intype = mlir::dyn_cast_or_null(inputPreTr.getType()); + LLVM_DEBUG({ llvm::dbgs() << "ConvConv intype after padding: " << intype << "\n"; }); + } int32_t stride_h = 1; int32_t stride_w = 1; diff --git a/circle-mlir/circle-mlir/lib/pass/src/ops/RangeOp.h b/circle-mlir/circle-mlir/lib/pass/src/ops/RangeOp.h new file mode 100644 index 00000000000..dae5f55c697 --- /dev/null +++ b/circle-mlir/circle-mlir/lib/pass/src/ops/RangeOp.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2026 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. + */ + +#ifndef __CIRCLE_MLIR_PASS_OPS_RANGE_OP_H__ +#define __CIRCLE_MLIR_PASS_OPS_RANGE_OP_H__ + +#include + +#include "ConvertHelper.h" + +#include + +#include + +namespace mlir +{ +namespace Circle +{ + +class ConvRange : public mlir::OpConversionPattern +{ +public: + using mlir::OpConversionPattern::OpConversionPattern; + using OpAdaptor = typename mlir::ONNXRangeOp::Adaptor; + + mlir::LogicalResult matchAndRewrite(mlir::ONNXRangeOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override + { + mlir::Value start = adaptor.getStart(); + mlir::Value limit = adaptor.getLimit(); + mlir::Value delta = adaptor.getDelta(); + + rewriter.replaceOpWithNewOp(op, op.getType(), start, limit, delta); + + return mlir::success(); + } +}; + +} // namespace Circle +} // namespace mlir + +#endif // __CIRCLE_MLIR_PASS_OPS_RANGE_OP_H__ diff --git a/circle-mlir/circle-mlir/lib/pass/src/ops/SignOp.h b/circle-mlir/circle-mlir/lib/pass/src/ops/SignOp.h new file mode 100644 index 00000000000..6c9e3de9782 --- /dev/null +++ b/circle-mlir/circle-mlir/lib/pass/src/ops/SignOp.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026 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. + */ + +#ifndef __CIRCLE_MLIR_PASS_OPS_SIGN_OP_H__ +#define __CIRCLE_MLIR_PASS_OPS_SIGN_OP_H__ + +#include + +#include "ConvertHelper.h" + +#include + +#include + +namespace mlir +{ +namespace Circle +{ + +class ConvSign : public mlir::OpConversionPattern +{ +public: + using mlir::OpConversionPattern::OpConversionPattern; + using OpAdaptor = typename mlir::ONNXSignOp::Adaptor; + + mlir::LogicalResult matchAndRewrite(mlir::ONNXSignOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override + { + mlir::Value input = adaptor.getInput(); + + rewriter.replaceOpWithNewOp(op, op.getType(), input); + + return mlir::success(); + } +}; + +} // namespace Circle +} // namespace mlir + +#endif // __CIRCLE_MLIR_PASS_OPS_SIGN_OP_H__ diff --git a/circle-mlir/circle-mlir/tools-test/circle-impexp-test/test.lst b/circle-mlir/circle-mlir/tools-test/circle-impexp-test/test.lst index e37456c4e97..afeb1d01aec 100644 --- a/circle-mlir/circle-mlir/tools-test/circle-impexp-test/test.lst +++ b/circle-mlir/circle-mlir/tools-test/circle-impexp-test/test.lst @@ -34,6 +34,7 @@ AddModel(Conv2d_F32_R4) AddModel(Conv2d_F32_R4_nobias) AddModel(Conv2d_F32_R4_k33) AddModel(Conv2d_F32_R4_p11) +AddModel(Conv2d_F32_R4_p11_g2) AddModel(Conv2d_F32_R4_p10) AddModel(Conv2d_F32_R4_g2) AddModel(Conv2d_F32_R4_g5) @@ -129,6 +130,9 @@ AddModel(Pow_F32_R2) AddModel(Pow_F32_R2_3) AddModel(Pow_F32_R4) AddModel(PReLU_F32_R4) +# AddModel(Range_F32_R0_1) --> Does't support dynamic shape output +# AddModel(Range_F32_R0_2) --> Does't support dynamic shape output +# AddModel(Range_F32_R0_3) --> Does't support dynamic shape output AddModel(Reciprocal_F32_R4) AddModel(ReduceMax_F32_R2) AddModel(ReduceMax_F32_R2_d0) @@ -166,6 +170,7 @@ AddModel(Resize_F32_R4_nearest) AddModel(Shape_F32_R4) AddModel(Sigmoid_F32_R2) AddModel(Sigmoid_F32_R4) +AddModel(Sign_F32_R4) AddModel(Sin_F32_R4) AddModel(Slice_F32_R2_4) # Rank2 of Slice_F32_R4_4 AddModel(Slice_F32_R3_4) # Rank3 of Slice_F32_R4_4 diff --git a/circle-mlir/circle-mlir/tools-test/onnx2circle-models/test.lst b/circle-mlir/circle-mlir/tools-test/onnx2circle-models/test.lst index 6f48bb927ad..05c3f9131ec 100644 --- a/circle-mlir/circle-mlir/tools-test/onnx2circle-models/test.lst +++ b/circle-mlir/circle-mlir/tools-test/onnx2circle-models/test.lst @@ -34,6 +34,7 @@ AddModel(Conv2d_F32_R4) AddModel(Conv2d_F32_R4_nobias) AddModel(Conv2d_F32_R4_k33) AddModel(Conv2d_F32_R4_p11) +AddModel(Conv2d_F32_R4_p11_g2) AddModel(Conv2d_F32_R4_p10) AddModel(Conv2d_F32_R4_g2) AddModel(Conv2d_F32_R4_g4_1) @@ -137,6 +138,9 @@ AddModel(QuantizeLinear_F32_R3_ui8) AddModel(QuantizeLinear_F32_R3_ui8_fq) AddModel(QuantizeLinear_F32_R4_i16_cw) AddModel(QuantizeLinear_F32_R4_ui8_cw) +AddModel(Range_F32_R0_1) +AddModel(Range_F32_R0_2) +AddModel(Range_F32_R0_3) AddModel(Reciprocal_F32_R4) AddModel(ReduceMax_F32_R2) AddModel(ReduceMax_F32_R2_d0) @@ -175,6 +179,7 @@ AddModel(Rsqrt_F32_R4) AddModel(Shape_F32_R4) AddModel(Sigmoid_F32_R2) AddModel(Sigmoid_F32_R4) +AddModel(Sign_F32_R4) AddModel(Sin_F32_R4) AddModel(Slice_F32_R2_4) # Rank2 of Slice_F32_R4_4 AddModel(Slice_F32_R3_4) # Rank3 of Slice_F32_R4_4 diff --git a/circle-mlir/circle-mlir/tools-test/onnx2circle-value-test/test.lst b/circle-mlir/circle-mlir/tools-test/onnx2circle-value-test/test.lst index 87c1aa9171b..d7feef42a74 100644 --- a/circle-mlir/circle-mlir/tools-test/onnx2circle-value-test/test.lst +++ b/circle-mlir/circle-mlir/tools-test/onnx2circle-value-test/test.lst @@ -34,6 +34,7 @@ AddModel(Conv2d_F32_R4) AddModel(Conv2d_F32_R4_nobias) AddModel(Conv2d_F32_R4_k33) AddModel(Conv2d_F32_R4_p11) +AddModel(Conv2d_F32_R4_p11_g2) AddModel(Conv2d_F32_R4_p10) AddModel(Conv2d_F32_R4_g2) AddModel(Conv2d_F32_R4_g4_1) @@ -137,6 +138,9 @@ AddModel(PReLU_F32_R4) # AddModel(QuantizeLinear_F32_R3_ui8_fq) # AddModel(QuantizeLinear_F32_R4_i16_cw) # AddModel(QuantizeLinear_F32_R4_ui8_cw) +# AddModel(Range_F32_R0_1) --> Does't support dynamic shape output +# AddModel(Range_F32_R0_2) --> Does't support dynamic shape output +# AddModel(Range_F32_R0_3) --> Does't support dynamic shape output AddModel(Reciprocal_F32_R4) AddModel(ReduceMax_F32_R2) AddModel(ReduceMax_F32_R2_d0) @@ -175,6 +179,7 @@ AddModel(Rsqrt_F32_R4) AddModel(Shape_F32_R4) AddModel(Sigmoid_F32_R2) AddModel(Sigmoid_F32_R4) +AddModel(Sign_F32_R4) AddModel(Sin_F32_R4) AddModel(Slice_F32_R2_4) # Rank2 of Slice_F32_R4_4 AddModel(Slice_F32_R3_4) # Rank3 of Slice_F32_R4_4 diff --git a/circle-mlir/circle-mlir/tools/onnx2circle/src/onnx2circle.cpp b/circle-mlir/circle-mlir/tools/onnx2circle/src/onnx2circle.cpp index fe8e3e4913c..ca5fce9bf1d 100644 --- a/circle-mlir/circle-mlir/tools/onnx2circle/src/onnx2circle.cpp +++ b/circle-mlir/circle-mlir/tools/onnx2circle/src/onnx2circle.cpp @@ -205,7 +205,7 @@ int convertToCircle(const O2Cparam ¶m) } // namespace onnx2circle // NOTE sync version number with 'infra/debian/*/changelog' for upgrade -const char *__version = "0.4.1"; +const char *__version = "0.4.2"; int entry(const O2Cparam ¶m) { diff --git a/circle-mlir/infra/debian/onnx2circle/changelog b/circle-mlir/infra/debian/onnx2circle/changelog index 31cd40160b2..f0761c9f4ba 100644 --- a/circle-mlir/infra/debian/onnx2circle/changelog +++ b/circle-mlir/infra/debian/onnx2circle/changelog @@ -1,3 +1,15 @@ +onnx2circle (0.4.2~202603040257~jammy) jammy; urgency=medium + + * Fix Conv2D shape with padding + + -- On-device AI developers Wed, 04 Mar 2026 03:08:07 +0000 + +onnx2circle (0.4.2~202602230830~jammy) jammy; urgency=medium + + * Introduce ReduceSumSquare and Range operations + + -- On-device AI developers Mon, 23 Feb 2026 08:40:46 +0000 + onnx2circle (0.4.1~jammy) jammy; urgency=medium * Fix GatherOp to accept both int32/int64 types diff --git a/circle-mlir/models/unit/Conv2d_F32_R4_p11_g2/__init__.py b/circle-mlir/models/unit/Conv2d_F32_R4_p11_g2/__init__.py new file mode 100644 index 00000000000..4b33b772574 --- /dev/null +++ b/circle-mlir/models/unit/Conv2d_F32_R4_p11_g2/__init__.py @@ -0,0 +1,20 @@ +import torch + + +# Generate Conv2d operator with Float32, Rank-4 with groups +class net_Conv2d(torch.nn.Module): + def __init__(self): + super().__init__() + self.op = torch.nn.Conv2d(4, 4, 1, padding=(1, 1), groups=2) + + def forward(self, input): + return self.op(input) + + def onnx_opset_version(self): + # TODO set to appropriate value + return 14 + + +_model_ = net_Conv2d() + +_inputs_ = torch.randn(1, 4, 3, 3) diff --git a/circle-mlir/models/unit/Range_F32_R0_1/__init__.py b/circle-mlir/models/unit/Range_F32_R0_1/__init__.py new file mode 100644 index 00000000000..fe14b3c8f96 --- /dev/null +++ b/circle-mlir/models/unit/Range_F32_R0_1/__init__.py @@ -0,0 +1,20 @@ +import torch + + +# Generate Range operator with Float32, scalar +class net_Range(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(self, input): + limit = input + return torch.arange(0, limit, 1, dtype=torch.float32) + + def onnx_opset_version(self): + return 11 + + +_model_ = net_Range() + +# produce float32 scalar with fixed number +_inputs_ = torch.tensor(10, dtype=torch.float32) diff --git a/circle-mlir/models/unit/Range_F32_R0_2/__init__.py b/circle-mlir/models/unit/Range_F32_R0_2/__init__.py new file mode 100644 index 00000000000..00a3ea63f31 --- /dev/null +++ b/circle-mlir/models/unit/Range_F32_R0_2/__init__.py @@ -0,0 +1,19 @@ +import torch + + +# Generate Range operator with Float32, scalar +class net_Range(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(self, limit, delta): + return torch.arange(0, limit, delta, dtype=torch.float32) + + def onnx_opset_version(self): + return 11 + + +_model_ = net_Range() + +# produce float32 scalar with fixed number +_inputs_ = (torch.tensor(10, dtype=torch.float32), torch.tensor(1, dtype=torch.float32)) diff --git a/circle-mlir/models/unit/Range_F32_R0_3/__init__.py b/circle-mlir/models/unit/Range_F32_R0_3/__init__.py new file mode 100644 index 00000000000..27aceaffb70 --- /dev/null +++ b/circle-mlir/models/unit/Range_F32_R0_3/__init__.py @@ -0,0 +1,20 @@ +import torch + + +# Generate Range operator with Float32, scalar +class net_Range(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(self, start, limit, delta): + return torch.arange(start, limit, delta, dtype=torch.float32) + + def onnx_opset_version(self): + return 11 + + +_model_ = net_Range() + +# produce float32 scalar with fixed number +_inputs_ = (torch.tensor(0, dtype=torch.float32), torch.tensor(10, dtype=torch.float32), + torch.tensor(1, dtype=torch.float32)) diff --git a/circle-mlir/models/unit/Sign_F32_R4/__init__.py b/circle-mlir/models/unit/Sign_F32_R4/__init__.py new file mode 100644 index 00000000000..7b9154bff5e --- /dev/null +++ b/circle-mlir/models/unit/Sign_F32_R4/__init__.py @@ -0,0 +1,19 @@ +import torch + + +# Generate Sign operator with Float32, Rank-4 +class net_Sign(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(self, input): + return torch.sign(input) + + def onnx_opset_version(self): + # TODO set to appropriate value + return 14 + + +_model_ = net_Sign() + +_inputs_ = torch.randn(1, 2, 3, 3) diff --git a/circle-mlir/models/unit/Unsqueeze_F32_R0/__init__.py b/circle-mlir/models/unit/Unsqueeze_F32_R0/__init__.py index 8c62b64a2e5..84034ed5469 100644 --- a/circle-mlir/models/unit/Unsqueeze_F32_R0/__init__.py +++ b/circle-mlir/models/unit/Unsqueeze_F32_R0/__init__.py @@ -16,5 +16,5 @@ def onnx_opset_version(self): _model_ = net_Unsqueeze() -# produuce float32 scalar +# produce float32 scalar _inputs_ = torch.randn(1)[0] diff --git a/circle-mlir/models/unit/Unsqueeze_F32_R0_v11/__init__.py b/circle-mlir/models/unit/Unsqueeze_F32_R0_v11/__init__.py index 28aa8aae68a..c2a78d5d846 100644 --- a/circle-mlir/models/unit/Unsqueeze_F32_R0_v11/__init__.py +++ b/circle-mlir/models/unit/Unsqueeze_F32_R0_v11/__init__.py @@ -15,5 +15,5 @@ def onnx_opset_version(self): _model_ = net_Unsqueeze() -# produuce float32 scalar +# produce float32 scalar _inputs_ = torch.randn(1)[0] diff --git a/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst b/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst index 8741745a86d..1fcad227d4a 100644 --- a/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst +++ b/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst @@ -74,6 +74,7 @@ REGISTER_KERNEL(Rsqrt) REGISTER_KERNEL(Select) REGISTER_KERNEL(SelectV2) REGISTER_KERNEL(Shape) +REGISTER_KERNEL(Sign) REGISTER_KERNEL(Sin) REGISTER_KERNEL(Slice) REGISTER_KERNEL(Softmax) diff --git a/compiler/luci-interpreter/src/kernels/Sign.cpp b/compiler/luci-interpreter/src/kernels/Sign.cpp new file mode 100644 index 00000000000..348eff672a2 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Sign.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2026 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. + */ + +#include "kernels/Sign.h" + +#include "kernels/Utils.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +namespace +{ + +template +inline void CalcSign(const T *input_data, const size_t num_elements, T *output_data) +{ + for (size_t i = 0; i < num_elements; ++i) + { + const T x = input_data[i]; + if constexpr (std::is_floating_point_v) + { + if (std::isnan(x)) + { + output_data[i] = x; // NaN -> NaN + continue; + } + } + output_data[i] = (T(0) < x) - (x < T(0)); // -1/0/1 + } +} + +} // namespace + +Sign::Sign(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Sign::configure() +{ + auto et = input()->element_type(); + LUCI_INTERPRETER_CHECK(et == DataType::FLOAT32 || et == DataType::FLOAT64 || et == DataType::S32); + LUCI_INTERPRETER_CHECK(et == output()->element_type()); + output()->resize(input()->shape()); +} + +void Sign::execute() const +{ + switch (input()->element_type()) + { + case DataType::S32: + evalS32(); + break; + case DataType::FLOAT32: + evalFloat32(); + break; + case DataType::FLOAT64: + evalFloat64(); + break; + default: + throw std::runtime_error("luci-intp Sign Unsupported type."); + } +} + +template void Sign::eval() const +{ + const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output())); + CalcSign(getTensorData(input()), static_cast(size), getTensorData(output())); +} + +void Sign::evalS32() const { eval(); } +void Sign::evalFloat32() const { eval(); } +void Sign::evalFloat64() const { eval(); } + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Sign.h b/compiler/luci-interpreter/src/kernels/Sign.h new file mode 100644 index 00000000000..689087f169b --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Sign.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2026 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. + */ + +#ifndef LUCI_INTERPRETER_KERNELS_SIGN_H +#define LUCI_INTERPRETER_KERNELS_SIGN_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Sign : public Kernel +{ +public: + Sign(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template void eval() const; + + void evalS32() const; + void evalFloat32() const; + void evalFloat64() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SIGN_H diff --git a/compiler/luci-interpreter/src/kernels/Sign.test.cpp b/compiler/luci-interpreter/src/kernels/Sign.test.cpp new file mode 100644 index 00000000000..7aa9668d64e --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Sign.test.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2026 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. + */ + +#include "kernels/Sign.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +#include + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +TEST(SignTest, S32) +{ + std::unique_ptr memory_manager = std::make_unique(); + + // 0, +, - + Shape input_shape{1, 1, 3}; + std::vector input_data{0, 2, -3}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + Sign kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + // shape check + std::vector ref_output_shape{1, 1, 3}; + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + + // data check + auto out = extractTensorData(output_tensor); + + EXPECT_THAT(std::vector(out.begin(), out.end()), ::testing::ElementsAre(0, 1, -1)); +} + +TEST(SignTest, Float32) +{ + std::unique_ptr memory_manager = std::make_unique(); + + // 0, +, -, NaN + Shape input_shape{1, 1, 4}; + std::vector input_data{0.0f, 2.0f, -3.0f, std::numeric_limits::quiet_NaN()}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Sign kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + // shape check + std::vector ref_output_shape{1, 1, 4}; + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + + // data check + auto out = extractTensorData(output_tensor); + + // first 3 are deterministic + std::vector ref_first3{0.0f, 1.0f, -1.0f}; + EXPECT_THAT(std::vector(out.begin(), out.begin() + 3), FloatArrayNear(ref_first3)); + + // NaN should stay NaN + EXPECT_TRUE(std::isnan(out[3])); +} + +TEST(SignTest, Float64) +{ + std::unique_ptr memory_manager = std::make_unique(); + + // 0, +, -, NaN + Shape input_shape{1, 1, 4}; + std::vector input_data{0.0, 2.0, -3.0, std::numeric_limits::quiet_NaN()}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT64); + + Sign kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + // shape check + std::vector ref_output_shape{1, 1, 4}; + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + + // data check + auto out = extractTensorData(output_tensor); + + // first 3 are deterministic + std::vector ref_first3{0.0, 1.0, -1.0}; + EXPECT_THAT(std::vector(out.begin(), out.begin() + 3), DoubleArrayNear(ref_first3)); + + // NaN should stay NaN + EXPECT_TRUE(std::isnan(out[3])); +} + +TEST(SignTest, InvalidDType_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Shape input_shape{1, 1, 3}; + std::vector input_data{1l, 2l, 3l}; + + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + Sign kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/TestUtils.cpp b/compiler/luci-interpreter/src/kernels/TestUtils.cpp index 4d983adda2a..da92fa01291 100644 --- a/compiler/luci-interpreter/src/kernels/TestUtils.cpp +++ b/compiler/luci-interpreter/src/kernels/TestUtils.cpp @@ -27,6 +27,7 @@ namespace testing { using ::testing::FloatNear; +using ::testing::DoubleNear; using ::testing::Matcher; Tensor makeOutputTensor(DataType element_type) { return Tensor(element_type, {}, {}, ""); } @@ -112,6 +113,18 @@ Matcher> FloatArrayNear(const std::vector &values, flo return ElementsAreArray(matchers); } +Matcher> DoubleArrayNear(const std::vector &values, + double max_abs_error) +{ + std::vector> matchers; + matchers.reserve(values.size()); + for (const double v : values) + { + matchers.emplace_back(DoubleNear(v, max_abs_error)); + } + return ElementsAreArray(matchers); +} + std::vector extractTensorShape(const Tensor &tensor) { std::vector result; diff --git a/compiler/luci-interpreter/src/kernels/TestUtils.h b/compiler/luci-interpreter/src/kernels/TestUtils.h index b9c942e9a34..f3050f859eb 100644 --- a/compiler/luci-interpreter/src/kernels/TestUtils.h +++ b/compiler/luci-interpreter/src/kernels/TestUtils.h @@ -170,6 +170,10 @@ std::vector dequantizeTensorData(const Tensor &tensor); ::testing::Matcher> FloatArrayNear(const std::vector &values, float max_abs_error = 1.0e-5f); +// Array version of `::testing::DoubleNear` matcher. +::testing::Matcher> DoubleArrayNear(const std::vector &values, + double max_abs_error = 1.0e-12); + template std::vector quantize(const float *data, size_t num_elements, float scale, int32_t zero_point) { diff --git a/compiler/luci-interpreter/src/loader/nodes/Sign.cpp b/compiler/luci-interpreter/src/loader/nodes/Sign.cpp new file mode 100644 index 00000000000..cde2d49593b --- /dev/null +++ b/compiler/luci-interpreter/src/loader/nodes/Sign.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026 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. + */ + +#include "Builders.h" + +#include "kernels/Sign.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSign(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); + + return std::make_unique(input, output); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-value-test/test.lst b/compiler/luci-value-test/test.lst index ea944fdf83e..309a1b28e8f 100644 --- a/compiler/luci-value-test/test.lst +++ b/compiler/luci-value-test/test.lst @@ -144,6 +144,7 @@ addeval(Select_002) #addeval(Shape_000) addeval(SignatureDef_MultiOut_000) addeval(SignatureDef_MultiOut_001) +addeval(Sign_000) addeval(Sin_000) addeval(Slice_000) addeval(Softmax_000) diff --git a/compiler/luci/export/src/CircleBuiltinTypesExtractor.h b/compiler/luci/export/src/CircleBuiltinTypesExtractor.h index c40a1315b40..16fd5dfc49e 100644 --- a/compiler/luci/export/src/CircleBuiltinTypesExtractor.h +++ b/compiler/luci/export/src/CircleBuiltinTypesExtractor.h @@ -409,6 +409,7 @@ class BuiltinOptionsExtractor final return circle::CreateShapeOptions(_builder, luci::to_circle_tensortype(node->out_type())) .Union(); } + flatbuffers::Offset visit(luci::CircleSign *) { return _no_option; } flatbuffers::Offset visit(luci::CircleSin *) { return _no_option; } flatbuffers::Offset visit(luci::CircleSlice *) { diff --git a/compiler/luci/export/src/CircleOps.lst b/compiler/luci/export/src/CircleOps.lst index 6f5787c92f4..fba1e6555ac 100644 --- a/compiler/luci/export/src/CircleOps.lst +++ b/compiler/luci/export/src/CircleOps.lst @@ -109,6 +109,7 @@ CIRCLE_NODE(CircleSegmentSum, BuiltinOperator_SEGMENT_SUM, BuiltinOptions_Segmen CIRCLE_NODE(CircleSelect, BuiltinOperator_SELECT, BuiltinOptions_SelectOptions) CIRCLE_NODE(CircleSelectV2, BuiltinOperator_SELECT_V2, BuiltinOptions_SelectV2Options) CIRCLE_NODE(CircleShape, BuiltinOperator_SHAPE, BuiltinOptions_ShapeOptions) +CIRCLE_NODE(CircleSign, BuiltinOperator_SIGN, BuiltinOptions_NONE) CIRCLE_NODE(CircleSin, BuiltinOperator_SIN, BuiltinOptions_NONE) CIRCLE_NODE(CircleSlice, BuiltinOperator_SLICE, BuiltinOptions_SliceOptions) CIRCLE_NODE(CircleSoftmax, BuiltinOperator_SOFTMAX, BuiltinOptions_SoftmaxOptions) diff --git a/compiler/luci/import/include/luci/Import/Nodes.h b/compiler/luci/import/include/luci/Import/Nodes.h index 981a6b86fe5..e0d279b0924 100644 --- a/compiler/luci/import/include/luci/Import/Nodes.h +++ b/compiler/luci/import/include/luci/Import/Nodes.h @@ -116,6 +116,7 @@ #include "Nodes/CircleSelect.h" #include "Nodes/CircleSelectV2.h" #include "Nodes/CircleShape.h" +#include "Nodes/CircleSign.h" #include "Nodes/CircleSin.h" #include "Nodes/CircleSlice.h" #include "Nodes/CircleSoftmax.h" diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSign.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSign.h new file mode 100644 index 00000000000..410c57f46d0 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSign.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2026 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. + */ + +#ifndef __LUCI_IMPORT_OP_CIRCLE_SIGN_H__ +#define __LUCI_IMPORT_OP_CIRCLE_SIGN_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleSignGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_SIGN_H__ diff --git a/compiler/luci/import/src/GraphBuilderRegistry.cpp b/compiler/luci/import/src/GraphBuilderRegistry.cpp index 6aa5fa1d203..0436dadf0d2 100644 --- a/compiler/luci/import/src/GraphBuilderRegistry.cpp +++ b/compiler/luci/import/src/GraphBuilderRegistry.cpp @@ -125,6 +125,7 @@ GraphBuilderRegistry::GraphBuilderRegistry() CIRCLE_NODE(SELECT, CircleSelectGraphBuilder); // 64 CIRCLE_NODE(SELECT_V2, CircleSelectV2GraphBuilder); // 123 CIRCLE_NODE(SHAPE, CircleShapeGraphBuilder); // 77 + CIRCLE_NODE(SIGN, CircleSignGraphBuilder); // 158 CIRCLE_NODE(SIN, CircleSinGraphBuilder); // 66 CIRCLE_NODE(SLICE, CircleSliceGraphBuilder); // 65 CIRCLE_NODE(SOFTMAX, CircleSoftmaxGraphBuilder); // 25 diff --git a/compiler/luci/import/src/Nodes/CircleSign.cpp b/compiler/luci/import/src/Nodes/CircleSign.cpp new file mode 100644 index 00000000000..a1c7b39188d --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleSign.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2026 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. + */ + +#include "luci/Import/Nodes/CircleSign.h" + +#include + +#include + +namespace luci +{ + +bool CircleSignGraphBuilder::validate(const ValidateArgs &args) const +{ + if (!GraphBuilder::validate(args, 1)) + return false; + + const auto &inputs = args.op.inputs; + // input type check + const auto tensors = args.reader.tensors(); + const auto tensor = tensors.at(inputs.at(0)); + assert(tensor != nullptr); + switch (tensor->type()) + { + case circle::TensorType_INT32: + case circle::TensorType_FLOAT32: + case circle::TensorType_FLOAT64: + break; + default: + return false; + } + + return true; +} + +CircleNode *CircleSignGraphBuilder::build_node(const circle::OperatorT &, + const std::vector &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + node->x(inputs.at(0)); + + // No options for Sign + + return node; +} + +} // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.h b/compiler/luci/lang/include/luci/IR/CircleNodes.h index 0cae7a8f205..3ee092ef9b7 100644 --- a/compiler/luci/lang/include/luci/IR/CircleNodes.h +++ b/compiler/luci/lang/include/luci/IR/CircleNodes.h @@ -109,6 +109,7 @@ #include "Nodes/CircleSelect.h" #include "Nodes/CircleSelectV2.h" #include "Nodes/CircleShape.h" +#include "Nodes/CircleSign.h" #include "Nodes/CircleSin.h" #include "Nodes/CircleSlice.h" #include "Nodes/CircleSoftmax.h" diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.lst b/compiler/luci/lang/include/luci/IR/CircleNodes.lst index 50ddfbcd7a9..d21a1ed482a 100644 --- a/compiler/luci/lang/include/luci/IR/CircleNodes.lst +++ b/compiler/luci/lang/include/luci/IR/CircleNodes.lst @@ -108,6 +108,7 @@ CIRCLE_NODE(SEGMENT_SUM, CircleSegmentSum) CIRCLE_NODE(SELECT, CircleSelect) CIRCLE_NODE(SELECT_V2, CircleSelectV2) CIRCLE_NODE(SHAPE, CircleShape) +CIRCLE_NODE(SIGN, CircleSign) CIRCLE_NODE(SIN, CircleSin) CIRCLE_NODE(SLICE, CircleSlice) CIRCLE_NODE(SOFTMAX, CircleSoftmax) diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSign.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSign.h new file mode 100644 index 00000000000..1d6539a2b64 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSign.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026 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. + */ + +#ifndef __LUCI_IR_CIRCLE_SIGN_H__ +#define __LUCI_IR_CIRCLE_SIGN_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/CircleNodeMixins.h" + +namespace luci +{ + +/** + * @brief SIGN in Circle + */ +class CircleSign final : public FixedArityNode<1, CircleNodeImpl> +{ +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLE_SIGN_H__ diff --git a/compiler/luci/lang/src/Nodes/CircleSign.test.cpp b/compiler/luci/lang/src/Nodes/CircleSign.test.cpp new file mode 100644 index 00000000000..85a81cd2fbe --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleSign.test.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026 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. + */ + +#include "luci/IR/Nodes/CircleSign.h" + +#include "luci/IR/CircleDialect.h" +#include "luci/IR/CircleNodeVisitor.h" + +#include + +TEST(CircleSignTest, constructor) +{ + luci::CircleSign sign_node; + + ASSERT_EQ(luci::CircleDialect::get(), sign_node.dialect()); + ASSERT_EQ(luci::CircleOpcode::SIGN, sign_node.opcode()); + + ASSERT_EQ(nullptr, sign_node.x()); +} + +TEST(CircleSignTest, input_NEG) +{ + luci::CircleSign sign_node; + luci::CircleSign node; + + sign_node.x(&node); + ASSERT_NE(nullptr, sign_node.x()); + + sign_node.x(nullptr); + ASSERT_EQ(nullptr, sign_node.x()); +} + +TEST(CircleSignTest, arity_NEG) +{ + luci::CircleSign sign_node; + + ASSERT_NO_THROW(sign_node.arg(0)); + ASSERT_THROW(sign_node.arg(1), std::out_of_range); +} + +TEST(CircleSignTest, visit_mutable_NEG) +{ + struct TestVisitor final : public luci::CircleNodeMutableVisitor + { + }; + + luci::CircleSign sign_node; + + TestVisitor tv; + ASSERT_THROW(sign_node.accept(&tv), std::exception); +} + +TEST(CircleSignTest, visit_NEG) +{ + struct TestVisitor final : public luci::CircleNodeVisitor + { + }; + + luci::CircleSign sign_node; + + TestVisitor tv; + ASSERT_THROW(sign_node.accept(&tv), std::exception); +} diff --git a/compiler/luci/logex/include/luci/CircleNodeSummaryBuilders.h b/compiler/luci/logex/include/luci/CircleNodeSummaryBuilders.h index 6884ef2e36e..9aec54af175 100644 --- a/compiler/luci/logex/include/luci/CircleNodeSummaryBuilders.h +++ b/compiler/luci/logex/include/luci/CircleNodeSummaryBuilders.h @@ -636,6 +636,10 @@ class CircleShapeSummaryBuilder final : public CircleNodeWithINPUTSummaryBuilder void build_attributes(const luci::CircleNode *node, locop::NodeSummary &s); }; +class CircleSignSummaryBuilder final : public CircleNodeWithXSummaryBuilder +{ +}; + class CircleSinSummaryBuilder final : public CircleNodeWithXSummaryBuilder { }; diff --git a/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp b/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp index 1768013bb0f..cb0f0baab44 100644 --- a/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp +++ b/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp @@ -232,6 +232,7 @@ CircleNodeSummaryBuilder::create_builder(const luci::CircleNode *node) CIRCLE_NODE(SELECT, CircleSelectSummaryBuilder) CIRCLE_NODE(SELECT_V2, CircleSelectV2SummaryBuilder) CIRCLE_NODE(SHAPE, CircleShapeSummaryBuilder) + CIRCLE_NODE(SIGN, CircleSignSummaryBuilder) CIRCLE_NODE(SIN, CircleSinSummaryBuilder) CIRCLE_NODE(SLICE, CircleSliceSummaryBuilder) CIRCLE_NODE(SOFTMAX, CircleSoftmaxSummaryBuilder) diff --git a/compiler/luci/partition/include/luci/ConnectNode.h b/compiler/luci/partition/include/luci/ConnectNode.h index edce0a1f272..b0a0176b340 100644 --- a/compiler/luci/partition/include/luci/ConnectNode.h +++ b/compiler/luci/partition/include/luci/ConnectNode.h @@ -154,6 +154,7 @@ class ConnectNode final : public luci::CircleNodeVisitor void visit(const luci::CircleSelect *) final; void visit(const luci::CircleSelectV2 *) final; void visit(const luci::CircleShape *) final; + void visit(const luci::CircleSign *) final; void visit(const luci::CircleSin *) final; void visit(const luci::CircleSlice *) final; void visit(const luci::CircleSoftmax *) final; diff --git a/compiler/luci/partition/src/Nodes/CircleSign.cpp b/compiler/luci/partition/src/Nodes/CircleSign.cpp new file mode 100644 index 00000000000..fbc70c44385 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleSign.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026 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. + */ + +#include "luci/ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleSign *node) +{ + auto *cloned = loco::must_cast(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast(node->x()); + + cloned->x(cn->find_clone(x)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleSign *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleSign.test.cpp b/compiler/luci/partition/src/Nodes/CircleSign.test.cpp new file mode 100644 index 00000000000..419160a82b7 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleSign.test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2026 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. + */ + +#include "luci/ConnectNode.h" + +#include "ConnectNode.test.h" + +#include + +#include + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT +{ +public: + NodeGraphlet() = default; +}; + +class TestNodeGraph : public TestIOGraph, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + NodeGraphlet::init(g()); + + node()->x(input()); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_Sign) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(1, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); +} + +TEST(ConnectNodeTest, connect_Sign_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/service/include/luci/Service/CircleShapeInference.h b/compiler/luci/service/include/luci/Service/CircleShapeInference.h index 0aaac56c620..82243c88947 100644 --- a/compiler/luci/service/include/luci/Service/CircleShapeInference.h +++ b/compiler/luci/service/include/luci/Service/CircleShapeInference.h @@ -134,6 +134,7 @@ class Algorithm final : public luci::CircleNodeVisitor // loco::TensorShape visit(const luci::CircleSelect *node) final; // loco::TensorShape visit(const luci::CircleSelectV2 *node) final; // loco::TensorShape visit(const luci::CircleShape *node) final; + // loco::TensorShape visit(const luci::CircleSign *node) final; // loco::TensorShape visit(const luci::CircleSin *node) final; // loco::TensorShape visit(const luci::CircleSlice *node) final; loco::TensorShape visit(const luci::CircleSoftmax *node) final; diff --git a/compiler/luci/service/include/luci/Service/CircleTypeInference.h b/compiler/luci/service/include/luci/Service/CircleTypeInference.h index 8db4c8f88a5..5311beeab3f 100644 --- a/compiler/luci/service/include/luci/Service/CircleTypeInference.h +++ b/compiler/luci/service/include/luci/Service/CircleTypeInference.h @@ -133,6 +133,7 @@ class Algorithm final : public luci::CircleNodeVisitor // loco::DataType visit(const luci::CircleSelect *node) final; // loco::DataType visit(const luci::CircleSelectV2 *node) final; // loco::DataType visit(const luci::CircleShape *node) final; + // loco::DataType visit(const luci::CircleSign *node) final; // loco::DataType visit(const luci::CircleSin *node) final; // loco::DataType visit(const luci::CircleSlice *node) final; // loco::DataType visit(const luci::CircleSoftmax *node) final; diff --git a/compiler/luci/service/src/CircleCloneNode.h b/compiler/luci/service/src/CircleCloneNode.h index 20b5f14ee04..fd38ded1167 100644 --- a/compiler/luci/service/src/CircleCloneNode.h +++ b/compiler/luci/service/src/CircleCloneNode.h @@ -201,6 +201,7 @@ template <> class CloneNodeLet final : public luci::CircleNodeVisitor< luci::CircleNode *visit(const luci::CircleSelect *) final; luci::CircleNode *visit(const luci::CircleSelectV2 *) final; luci::CircleNode *visit(const luci::CircleShape *) final; + luci::CircleNode *visit(const luci::CircleSign *) final; luci::CircleNode *visit(const luci::CircleSin *) final; luci::CircleNode *visit(const luci::CircleSlice *) final; luci::CircleNode *visit(const luci::CircleSoftmax *) final; diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.cpp index 321696384d8..9376aeea472 100644 --- a/compiler/luci/service/src/CircleShapeInferenceRule.cpp +++ b/compiler/luci/service/src/CircleShapeInferenceRule.cpp @@ -2084,6 +2084,8 @@ class ShapeInferenceAlgorithm final : public luci::CircleNodeVisitorout_type(); } + loco::DataType visit(const luci::CircleSign *node) final { return luci::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleSin *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleSlice *node) final diff --git a/compiler/luci/service/src/Nodes/CircleSign.cpp b/compiler/luci/service/src/Nodes/CircleSign.cpp new file mode 100644 index 00000000000..5fa7aed3681 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSign.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2026 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. + */ + +#include "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNodeLet::visit(const luci::CircleSign *) +{ + return _graph->nodes()->create(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSign.test.cpp b/compiler/luci/service/src/Nodes/CircleSign.test.cpp new file mode 100644 index 00000000000..8c117a3b707 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSign.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026 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. + */ + +#include "luci/Service/CircleNodeClone.h" + +#include + +TEST(CloneNodeTest, clone_Sign) +{ + auto g = loco::make_graph(); + auto node_sign = g->nodes()->create(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sign, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_sign = dynamic_cast(cloned); + ASSERT_NE(nullptr, cloned_sign); +} diff --git a/compiler/luci/tests/test.lst b/compiler/luci/tests/test.lst index 67b40c8038e..4f631ddc64a 100644 --- a/compiler/luci/tests/test.lst +++ b/compiler/luci/tests/test.lst @@ -159,6 +159,7 @@ addread(SelectV2_000) addread(SelectV2_001) addread(SelectV2_002) addread(Shape_000) +addread(Sign_000) addread(Sin_000) addread(Slice_000) addread(Slice_001) @@ -392,6 +393,7 @@ addwrite(SelectV2_000) addwrite(SelectV2_001) addwrite(SelectV2_002) addwrite(Shape_000) +addwrite(Sign_000) addwrite(Sin_000) addwrite(Slice_000) addwrite(Slice_001) diff --git a/compiler/tflchef/core/src/Op/Sign.cpp b/compiler/tflchef/core/src/Op/Sign.cpp new file mode 100644 index 00000000000..7ec3b2b3784 --- /dev/null +++ b/compiler/tflchef/core/src/Op/Sign.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2026 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. + */ + +#include "Sign.h" + +flatbuffers::Offset SignChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + return flatbuffers::Offset(); +} + +std::unique_ptr SignChefFactory::create(const tflchef::Operation *operation) const +{ + return std::unique_ptr{new SignChef{operation}}; +} diff --git a/compiler/tflchef/core/src/Op/Sign.h b/compiler/tflchef/core/src/Op/Sign.h new file mode 100644 index 00000000000..f3cfe4f8f8d --- /dev/null +++ b/compiler/tflchef/core/src/Op/Sign.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2026 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. + */ + +#ifndef __OP_SIGN_H__ +#define __OP_SIGN_H__ + +#include "OpChef.h" + +class SignChef final : public OpChef +{ +public: + explicit SignChef(const tflchef::Operation *operation) : _operation{operation} + { + // DO NOTHING + } + +public: + tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SIGN; } + + tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; } + + flatbuffers::Offset value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const tflchef::Operation *_operation; +}; + +struct SignChefFactory final : public OpChefFactory +{ + std::unique_ptr create(const tflchef::Operation *operation) const override; +}; + +#endif // __OP_SIGN_H__ diff --git a/compiler/tflchef/core/src/OpChef.def b/compiler/tflchef/core/src/OpChef.def index 8881451e879..e74c1211ccc 100644 --- a/compiler/tflchef/core/src/OpChef.def +++ b/compiler/tflchef/core/src/OpChef.def @@ -95,6 +95,7 @@ OP_CHEF(SegmentSum,SegmentSumChefFactory) OP_CHEF(Select, SelectChefFactory) OP_CHEF(SelectV2, SelectV2ChefFactory) OP_CHEF(Shape, ShapeChefFactory) +OP_CHEF(Sign, SignChefFactory) OP_CHEF(Sin, SinChefFactory) OP_CHEF(Slice, SliceChefFactory) OP_CHEF(Softmax, SoftmaxChefFactory) diff --git a/compiler/tflchef/core/src/OpChefs.h b/compiler/tflchef/core/src/OpChefs.h index 2c2b9be67a6..1e2598f513c 100644 --- a/compiler/tflchef/core/src/OpChefs.h +++ b/compiler/tflchef/core/src/OpChefs.h @@ -108,6 +108,7 @@ #include "Op/Select.h" #include "Op/SelectV2.h" #include "Op/Shape.h" +#include "Op/Sign.h" #include "Op/Sin.h" #include "Op/Slice.h" #include "Op/Softmax.h" diff --git a/compiler/tflchef/tflite/src/Op/Sign.cpp b/compiler/tflchef/tflite/src/Op/Sign.cpp new file mode 100644 index 00000000000..96ce4081636 --- /dev/null +++ b/compiler/tflchef/tflite/src/Op/Sign.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026 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. + */ + +#include "Sign.h" + +#include "Convert.h" + +namespace tflchef +{ + +void TFliteOpSign::filler(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const +{ + // Nothing to do with filler +} + +tflchef::Operation *TFliteOpSign::build(RecipeChefContext *ctx) const +{ + tflchef::Operation *operation = ctx->chefop; + const tflite::Operator *op = ctx->tflop; + + operation->set_type("Sign"); + + return operation; +} + +} // namespace tflchef diff --git a/compiler/tflchef/tflite/src/Op/include/Sign.h b/compiler/tflchef/tflite/src/Op/include/Sign.h new file mode 100644 index 00000000000..827633543e4 --- /dev/null +++ b/compiler/tflchef/tflite/src/Op/include/Sign.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026 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. + */ + +#ifndef __TFLITE_OP_SIGN_H__ +#define __TFLITE_OP_SIGN_H__ + +#include "TFliteOpChef.h" + +namespace tflchef +{ + +/** + * @brief tflchef operator builder for Sign + */ +class TFliteOpSign : public TFliteOpChef +{ +public: + void filler(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const override; + tflchef::Operation *build(RecipeChefContext *ctx) const override; +}; + +} // namespace tflchef + +#endif // __TFLITE_OP_SIGN_H__ diff --git a/compiler/tflchef/tflite/src/TFliteOpChefs.h b/compiler/tflchef/tflite/src/TFliteOpChefs.h index 94e2ad25c26..ece52d3a633 100644 --- a/compiler/tflchef/tflite/src/TFliteOpChefs.h +++ b/compiler/tflchef/tflite/src/TFliteOpChefs.h @@ -107,6 +107,7 @@ #include "Op/include/Select.h" #include "Op/include/SelectV2.h" #include "Op/include/Shape.h" +#include "Op/include/Sign.h" #include "Op/include/Sin.h" #include "Op/include/Slice.h" #include "Op/include/Softmax.h" diff --git a/compiler/tflchef/tflite/src/TFliteOpRegistry.h b/compiler/tflchef/tflite/src/TFliteOpRegistry.h index 43d526d3427..2ce83cd4a83 100644 --- a/compiler/tflchef/tflite/src/TFliteOpRegistry.h +++ b/compiler/tflchef/tflite/src/TFliteOpRegistry.h @@ -144,6 +144,7 @@ class TFliteOpRegistry REG_TFL_OP(SELECT, TFliteOpSelect); REG_TFL_OP(SELECT_V2, TFliteOpSelectV2); REG_TFL_OP(SHAPE, TFliteOpShape); + REG_TFL_OP(SIGN, TFliteOpSign); REG_TFL_OP(SIN, TFliteOpSin); REG_TFL_OP(SLICE, TFliteOpSlice); REG_TFL_OP(SOFTMAX, TFliteOpSoftmax); diff --git a/compiler/tflite2circle/src/TFLOperator.lst b/compiler/tflite2circle/src/TFLOperator.lst index cbe171e006c..853f86d1db3 100644 --- a/compiler/tflite2circle/src/TFLOperator.lst +++ b/compiler/tflite2circle/src/TFLOperator.lst @@ -153,3 +153,4 @@ TFL_OPERATOR(BROADCAST_ARGS) TFL_OPERATOR(RANDOM_STANDARD_NORMAL) TFL_OPERATOR(GELU) TFL_OPERATOR(RELU_0_TO_1) +TFL_OPERATOR(SIGN) diff --git a/infra/command/copyright-check b/infra/command/copyright-check deleted file mode 100644 index 10c68ebe219..00000000000 --- a/infra/command/copyright-check +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -INVALID_EXIT=0 - -# Files to check copyright headers -# TODO Check python files as well -FILE_PATTERNS=(*.h *.hpp *.cpp *.cc *.c *.cl) - -# Manually ignore checking - 3rd party, generated files -# Pattern should start with ':!' to exclude pattern -FILE_EXCLUDE_PATTERN=( - :!compiler/ann-api - :!onert-micro/externals - :!runtime/3rdparty - :!runtime/tests/nnapi -) - -check_copyright() { - CORRECT_COPYRIGHT="Copyright \(c\) [0-9\-]+ Samsung Electronics Co\., Ltd\. All Rights Reserved" - FILES_TO_CHECK_COPYRIGHTS=$(git ls-files -c --exclude-standard -- ${FILE_PATTERNS[@]} ${FILE_EXCLUDE_PATTERN[@]}) - - if [[ ${#FILES_TO_CHECK_COPYRIGHTS} -ne 0 ]]; then - for f in ${FILES_TO_CHECK_COPYRIGHTS[@]}; do - if ! grep -qE "$CORRECT_COPYRIGHT" $f; then - CREATED_YEAR=$(git log --follow --format=%aD $f | tail -1 | awk '{print $4}') - EXAMPLE_COPYRIGHT="Copyright (c) $CREATED_YEAR Samsung Electronics Co., Ltd. All Rights Reserved" - echo "Copyright format of $f is incorrect: recommend \"$EXAMPLE_COPYRIGHT\"" - INVALID_EXIT=1 - fi - done - fi -} - -check_copyright - -if [[ $INVALID_EXIT -ne 0 ]]; then - echo "[FAILED] Invalid copyright check exit." - exit 1 -fi diff --git a/infra/command/format b/infra/command/format deleted file mode 100644 index 8d2089523e4..00000000000 --- a/infra/command/format +++ /dev/null @@ -1,234 +0,0 @@ -#!/bin/bash - -INVALID_EXIT=0 -FILES_TO_CHECK=() -DIRECTORIES_TO_BE_TESTED=() -DEFAULT_CLANG_VERSION="16" -CLANG_FORMAT_CANDIDATE=clang-format-$DEFAULT_CLANG_VERSION -PATCH_FILE=format.patch -CHECK_DIFF_ONLY="0" -CHECK_STAGED_ONLY="0" - -echo "[CAUTION!!!] This command will be deprecated. Please use pre-commit framework hooks instead. " -echo "See docs/howto/how-to-run-format-checker.md for more details." -echo "" - -function Usage() -{ - echo "Usage: $0 $(basename "${BASH_SOURCE[0]}") [OPTIONS] [ ...]" - echo "If no arguments are specified, it formats all nnas codes" - echo "If s are given, it reformats the files" - echo "" - echo "Options:" - echo " --clang-format-version clang format version (default: ${DEFAULT_CLANG_VERSION})" - echo " --diff-only check diff files with master" - echo " --staged-only check git staged files" -} - -while [[ $# -gt 0 ]] -do - arg="$1" - case $arg in - -h|--help|help) - Usage - exit 0 - ;; - --clang-format-version) - CLANG_FORMAT_CANDIDATE=clang-format-$2 - shift 2 - ;; - --staged-only) - CHECK_STAGED_ONLY="1" - CHECK_DIFF_ONLY="1" - shift - ;; - --diff-only) - CHECK_DIFF_ONLY="1" - shift - ;; - *) - DIRECTORIES_TO_BE_TESTED+=("$1") - shift - ;; - esac -done - -function command_exists() { - command -v "$1" > /dev/null 2>&1 -} - -function check_endoffile() { - # Exclude binary (refer .gitattributes file) - # TODO Remove svg file excluding - # .svg: xml type ML for vector graphic - FILES_TO_CHECK_EOF=$(echo "${FILES_TO_CHECK[@]}" | tr ' ' '\n' | grep -Ev '((\.bmp)|(\.caffemodel)|(\.circle)|(\.h5)|(\.jar)|(\.pdf)|(\.png)|(\.tar.gz)|(\.tflite)|(\.svg))$') - - echo "${FILES_TO_CHECK_EOF[@]}" | xargs -P "$(nproc)" -I {} bash -c "if [[ -n \"\$(tail -c1 {})\" ]]; then echo >> {}; fi" -} - -function check_permission() { - # Check all files except script - # Manually ignore permission checking - mapfile -t FILES_TO_CHECK_PERMISSION < <( - git ls-files -c -s --exclude-standard "${FILES_TO_CHECK[@]}" | - grep -E '^100755' | - cut -f2 | - grep -Ev '((^nnas)|(^nnfw)|(^nncc)|(\.sh)|(\.py)|(/gradlew))$' | - grep -Ev '((^infra/debian/compiler/rules)|(^infra/debian/runtime/rules))$' - ) - - # Add python specs files - mapfile -t TEMP_SPECS < <(printf '%s\n' "${FILES_TO_CHECK[@]}" | grep -E '^runtime/tests/nnapi/specs/.*.py$') - FILES_TO_CHECK_PERMISSION+=("${TEMP_SPECS[@]}") - - if [[ ${#FILES_TO_CHECK_PERMISSION[@]} -eq 0 ]]; then - return - fi - for f in "${FILES_TO_CHECK_PERMISSION[@]}"; do - chmod a-x "$f" - done -} - -function check_cpp_files() { - if [[ ${__Check_CPP} -eq 0 ]]; then - echo "[SKIPPED] C/C++ check is skipped" - return - fi - - if command_exists "$CLANG_FORMAT_CANDIDATE" ; then - CLANG_FORMAT=$CLANG_FORMAT_CANDIDATE - fi - - if [[ -z "${CLANG_FORMAT}" ]]; then - echo "[ERROR] $CLANG_FORMAT_CANDIDATE is unavailable" - echo - echo " Please install $CLANG_FORMAT_MIGRATE before running format check (refer https://github.com/Samsung/ONE/issues/12311#issuecomment-1857503157)" - echo " If you use docker for format checking, please pull or build latest version" - exit 1 - fi - - # Check c++ files - mapfile -t FILES_TO_CHECK_CPP < <(printf '%s\n' "${FILES_TO_CHECK[@]}" | grep -E '((\.c[cl]?)|(\.cpp)|(\.h(pp)?))$') - - if [[ ${#FILES_TO_CHECK_CPP} -ne 0 ]]; then - ${CLANG_FORMAT} -i "${FILES_TO_CHECK_CPP[@]}" - EXIT_CODE=$? - if [[ ${EXIT_CODE} -ne 0 ]]; then - INVALID_EXIT=${EXIT_CODE} - fi - fi -} - -function check_python_files() { - if [[ ${__Check_PYTHON} -eq 0 ]]; then - echo "[SKIPPED] Python check is skipped" - return - fi - - if ! command_exists yapf; then - echo "[ERROR] yapf is unavailable" - echo " Please install yapf." - exit 1 - fi - - # Check python files - mapfile -t FILES_TO_CHECK_PYTHON < <(printf '%s\n' "${FILES_TO_CHECK[@]}" | grep -E '\.py$') - - # Exceptional case: one-cmds don't have '.py' extension: ignore non-python source (cmake, etc) and ignore shell script: one-prepare-venv - mapfile -t EXCEPTIONAL_FILES < <(printf '%s\n' "${FILES_TO_CHECK[@]}" | - grep -E '^compiler/one-cmds/[^(\./)]*$' | grep -Ev '^compiler/one-cmds/one-prepare-venv$') - FILES_TO_CHECK_PYTHON+=("${EXCEPTIONAL_FILES[@]}") - - # Exceptional case: onecc-docker don't have '.py' extension. - mapfile -t EXCEPTIONAL_FILES < <(printf '%s\n' "${FILES_TO_CHECK[@]}" | grep -E '^compiler/onecc-docker/onecc-docker$') - FILES_TO_CHECK_PYTHON+=("${EXCEPTIONAL_FILES[@]}") - - # Exceptional case: visq don't have '.py' extension. - mapfile -t EXCEPTIONAL_FILES < <(printf '%s\n' "${FILES_TO_CHECK[@]}" | grep -E '^compiler/visq/visq$') - FILES_TO_CHECK_PYTHON+=("${EXCEPTIONAL_FILES[@]}") - - # Exceptional case: fm-equalize doesn't have '.py' extension. - mapfile -t EXCEPTIONAL_FILES < <(printf '%s\n' "${FILES_TO_CHECK[@]}" | grep -E '^compiler/fm-equalize/fm-equalize$') - FILES_TO_CHECK_PYTHON+=("${EXCEPTIONAL_FILES[@]}") - - if [[ ${#FILES_TO_CHECK_PYTHON} -ne 0 ]]; then - yapf -p -i "${FILES_TO_CHECK_PYTHON[@]}" - EXIT_CODE=$? - if [[ ${EXIT_CODE} -ne 0 ]]; then - INVALID_EXIT=${EXIT_CODE} - fi - fi -} - -cd "${NNAS_PROJECT_PATH}" || exit - -if [[ -n "$(git diff)" ]] && { [[ "${CHECK_DIFF_ONLY}" != "1" ]] || [[ "${CHECK_STAGED_ONLY}" != "1" ]]; }; then - echo "[WARNING] Commit all the changes before running format check" - echo " ${PATCH_FILE} file will contain unstaged files" -fi - -__Check_CPP=${CHECK_CPP:-"1"} -__Check_PYTHON=${CHECK_PYTHON:-"1"} - -# Git file mode -# 120000: symbolic link -# 160000: git link -# 100755: regular executable -# 100644: regular readable -# Reference: https://github.com/git/git/blob/cd42415/Documentation/technical/index-format.txt#L72-L81 - mapfile -t FILES_TO_CHECK < <(git ls-files -c -s --exclude-standard "${DIRECTORIES_TO_BE_TESTED[@]}" | grep -Ev '^1[26]0000' | cut -f2) -if [[ "${CHECK_DIFF_ONLY}" = "1" ]]; then - MASTER_EXIST=$(git rev-parse --verify master) - CURRENT_BRANCH=$(git branch | grep '^\*' | cut -d ' ' -f2-) - DIFF_COMMITS=$(git log --graph --oneline master..HEAD | wc -l) - if [[ -z "${MASTER_EXIST}" ]]; then - echo "Cannot found local master branch" - elif [[ "${CURRENT_BRANCH}" = "master" ]]; then - echo "Current branch is master" - else - if [[ "${CHECK_STAGED_ONLY}" = "1" ]]; then - mapfile -t FILES_TO_CHECK < <(git diff --staged --name-only --diff-filter=d) - else - mapfile -t FILES_TO_CHECK < <(git diff --name-only --diff-filter=d HEAD~"${DIFF_COMMITS}") - fi - mapfile -t FILES_TO_CHECK < <(git ls-files -c -s --exclude-standard "${FILES_TO_CHECK[@]}" | grep -Ev '^1[26]0000' | cut -f2) - fi -fi - -check_endoffile -check_permission -check_cpp_files -check_python_files - -if [[ "${CHECK_DIFF_ONLY}" = "1" ]] && [[ "${CHECK_STAGED_ONLY}" = "1" ]]; then - if [[ ${#FILES_TO_CHECK[@]} -ne 0 ]]; then - DIFF=$(git diff "${FILES_TO_CHECK[@]}" | tee ${PATCH_FILE}) - fi -else - DIFF=$(git diff | tee ${PATCH_FILE}) -fi - -cd ~- || exit - -if [[ -z "${CRCHECK}" ]] && [[ -z "${DIFF}" ]] && [[ ${INVALID_EXIT} -eq 0 ]]; then - echo "[PASSED] Format checker succeed." - return -fi - -# Something went wrong - -if [[ -n "${CRCHECK}" ]]; then - echo "[FAILED] Please use LF for newline for following files." - echo "${CRCHECK}" -fi - -if [[ -s ${PATCH_FILE} ]]; then - echo "[FAILED] Format checker failed and update code to follow convention." - echo " You can find changes in ${PATCH_FILE}" -fi - -if [[ ${INVALID_EXIT} -ne 0 ]]; then - echo "[FAILED] Invalid format checker exit." -fi - -exit 1 diff --git a/infra/debian/circle-interpreter/changelog b/infra/debian/circle-interpreter/changelog index b140c21afbe..4273e9d775e 100644 --- a/infra/debian/circle-interpreter/changelog +++ b/infra/debian/circle-interpreter/changelog @@ -1,3 +1,9 @@ +circle-interpreter (1.30.1~202602260919~jammy) jammy; urgency=medium + + * Support the Sign operation. + + -- On-device AI developers Thu, 26 Feb 2026 09:30:53 +0000 + circle-interpreter (1.30.0~202506190655~jammy) jammy; urgency=medium * Enable publish for noble diff --git a/infra/git-hooks/copyright-check.sh b/infra/git-hooks/copyright-check.sh new file mode 100755 index 00000000000..45c85eadf30 --- /dev/null +++ b/infra/git-hooks/copyright-check.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +CORRECT_COPYRIGHT="Copyright \(c\) [0-9\-]+ Samsung Electronics Co\., Ltd\. All Rights Reserved" + +if [ $# -eq 0 ]; then + echo "Please pass file(s) to check copyright" + exit 1 +fi + +for f in "$@"; do + if ! grep -qE "$CORRECT_COPYRIGHT" "$f"; then + CREATED_YEAR=$(git log --follow --format=%aD "$f" | tail -1 | awk '{print $4}') + EXAMPLE_COPYRIGHT="Copyright (c) $CREATED_YEAR Samsung Electronics Co., Ltd. All Rights Reserved" + echo "Copyright format of $f is incorrect: recommend \"$EXAMPLE_COPYRIGHT\"" + INVALID_EXIT=1 + fi +done + +if [[ $INVALID_EXIT -ne 0 ]]; then + exit 1 +fi diff --git a/infra/git-hooks/pre-commit.sh b/infra/git-hooks/pre-commit.sh deleted file mode 100755 index b349474a525..00000000000 --- a/infra/git-hooks/pre-commit.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -# An example hook script to verify what is about to be pushed. Called by "git -# push" after it has checked the remote status, but before anything has been -# pushed. If this script exits with a non-zero status nothing will be pushed. -# -# This hook is called with the following parameters: -# -# $1 -- Name of the remote to which the push is being done -# $2 -- URL to which the push is being done -# -# If pushing without using a named remote those arguments will be equal. -# -# Information about the commits which are being pushed is supplied as lines to -# the standard input in the form: -# -# -# -# This sample shows how to prevent push of commits where the log message starts -# with "WIP" (work in progress). - -remote="$1" -url="$2" - -# RUN FORMAT CHECKER - -echo "This hook will be deprecated. Please use pre-commit framework hooks instead. " -echo "See docs/howto/how-to-run-format-checker.md for more details." - -REPO_PATH=$(git rev-parse --show-toplevel) -cd $REPO_PATH - -./nnas format --staged-only - -exit $? diff --git a/infra/git-hooks/pre-push.sh b/infra/git-hooks/pre-push.sh deleted file mode 100755 index 64ea9c41ddd..00000000000 --- a/infra/git-hooks/pre-push.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -# An example hook script to verify what is about to be pushed. Called by "git -# push" after it has checked the remote status, but before anything has been -# pushed. If this script exits with a non-zero status nothing will be pushed. -# -# This hook is called with the following parameters: -# -# $1 -- Name of the remote to which the push is being done -# $2 -- URL to which the push is being done -# -# If pushing without using a named remote those arguments will be equal. -# -# Information about the commits which are being pushed is supplied as lines to -# the standard input in the form: -# -# -# -# This sample shows how to prevent push of commits where the log message starts -# with "WIP" (work in progress). - -remote="$1" -url="$2" - -# RUN FORMAT CHECKER - -echo "This hook will be deprecated. Please use pre-commit framework hooks instead. " -echo "See docs/howto/how-to-run-format-checker.md for more details." - -REPO_PATH=$(git rev-parse --show-toplevel) -cd $REPO_PATH - -./nnas format --diff-only - -exit $? diff --git a/onert-micro/onert-micro/include/OMStatus.h b/onert-micro/onert-micro/include/OMStatus.h index 66005fbe589..01a765dc3d8 100644 --- a/onert-micro/onert-micro/include/OMStatus.h +++ b/onert-micro/onert-micro/include/OMStatus.h @@ -35,6 +35,7 @@ enum OMStatus FailReadWOFFile, FailReadCheckpointFile, CmsisNNError, + IndexError, }; } // namespace onert_micro diff --git a/onert-micro/onert-micro/include/core/OMRuntimeShape.h b/onert-micro/onert-micro/include/core/OMRuntimeShape.h index 8f75012bf5f..ee33aa30d28 100644 --- a/onert-micro/onert-micro/include/core/OMRuntimeShape.h +++ b/onert-micro/onert-micro/include/core/OMRuntimeShape.h @@ -123,9 +123,13 @@ class OMRuntimeShape if (_size == 0) return 0; - auto it = _dims.cbegin(); - - return std::accumulate(it, it + _size, 1u, std::multiplies()); + size_t result = 1; + for (size_t i = 0; i < _size; ++i) + { + if (__builtin_mul_overflow(result, static_cast(_dims[i]), &result)) + return 0; + } + return result; } // clang-format off diff --git a/onert-micro/onert-micro/include/pal/common/PALGatherND.h b/onert-micro/onert-micro/include/pal/common/PALGatherND.h index 883b4771f64..bc0286bcce1 100644 --- a/onert-micro/onert-micro/include/pal/common/PALGatherND.h +++ b/onert-micro/onert-micro/include/pal/common/PALGatherND.h @@ -72,6 +72,13 @@ inline OMStatus GatherND(core::OMRuntimeShape params_shape, const ParamsT *param { int offset = i * indices_nd + j; IndicesT index = index_data[offset]; + + // Bounds check: index must be non-negative and within dimension size + if (index < 0 || index >= params_shape.dims(j)) + { + return IndexError; + } + from_pos += index * dims_to_count[j]; } if (from_pos < 0 || from_pos + slice_size > params_flat_size) diff --git a/onert-micro/onert-micro/include/pal/common/PALSlice.h b/onert-micro/onert-micro/include/pal/common/PALSlice.h index 234fd9ec780..b1d1bf943ff 100644 --- a/onert-micro/onert-micro/include/pal/common/PALSlice.h +++ b/onert-micro/onert-micro/include/pal/common/PALSlice.h @@ -42,6 +42,18 @@ OMStatus Slice(const core::SliceParams &op_params, const core::OMRuntimeShape &i stop[i] = (size_count < padded_i || op_params.size[size_count - padded_i] == -1) ? ext_shape.dims(i) : start[i] + op_params.size[size_count - padded_i]; + + // Bounds check: start must be non-negative and within dimension size + if (start[i] < 0 || start[i] > ext_shape.dims(i)) + { + return IndexError; + } + + // Bounds check: stop must be within dimension size and >= start + if (stop[i] < start[i] || stop[i] > ext_shape.dims(i)) + { + return IndexError; + } } for (int i0 = start[0]; i0 < stop[0]; ++i0) diff --git a/onert-micro/onert-micro/src/execute/kernels/Gather.cpp b/onert-micro/onert-micro/src/execute/kernels/Gather.cpp index 5c8504c7122..6b86fb944dd 100644 --- a/onert-micro/onert-micro/src/execute/kernels/Gather.cpp +++ b/onert-micro/onert-micro/src/execute/kernels/Gather.cpp @@ -36,9 +36,9 @@ constexpr uint32_t positionsTensorIdx = 1; constexpr uint32_t outputTensorIdx = 0; template -void gather(const InputT *input_data, const CoordsT *coords_data, InputT *output_data, - int32_t axis_size, int32_t batch_size, int32_t outer_size, int32_t inner_size, - int32_t coord_size) +OMStatus gather(const InputT *input_data, const CoordsT *coords_data, InputT *output_data, + int32_t axis_size, int32_t batch_size, int32_t outer_size, int32_t inner_size, + int32_t coord_size) { for (int batch = 0; batch < batch_size; ++batch) @@ -47,7 +47,14 @@ void gather(const InputT *input_data, const CoordsT *coords_data, InputT *output { for (int coord = 0; coord < coord_size; ++coord) { - auto x = coords_data[coord]; + auto x = coords_data[batch * coord_size + coord]; + + // Bounds check: index must be in range [0, axis_size) + if (x < 0 || x >= axis_size) + { + return IndexError; + } + std::memcpy( output_data + (((batch * outer_size) + outer) * coord_size + coord) * inner_size, input_data + @@ -57,6 +64,8 @@ void gather(const InputT *input_data, const CoordsT *coords_data, InputT *output } } } + + return Ok; } } // namespace @@ -159,29 +168,29 @@ OMStatus execute_kernel_CircleGather(const OMExecuteArgs &execute_args) #ifndef DIS_FLOAT case circle::TensorType_FLOAT32: { - gather(utils::castInputData(input_data), - utils::castInputData(position_data), - utils::castOutputData(output_data), axis_size, batch_size, - outer_size, inner_size, coord_size); + status = gather(utils::castInputData(input_data), + utils::castInputData(position_data), + utils::castOutputData(output_data), axis_size, + batch_size, outer_size, inner_size, coord_size); } break; #endif // DIS_FLOAT #ifndef DIS_QUANT case circle::TensorType_INT8: { - gather(utils::castInputData(input_data), - utils::castInputData(position_data), - utils::castOutputData(output_data), axis_size, batch_size, - outer_size, inner_size, coord_size); + status = gather(utils::castInputData(input_data), + utils::castInputData(position_data), + utils::castOutputData(output_data), axis_size, + batch_size, outer_size, inner_size, coord_size); } break; #endif // DIS_QUANT case circle::TensorType_INT32: { - gather(utils::castInputData(input_data), - utils::castInputData(position_data), - utils::castOutputData(output_data), axis_size, batch_size, - outer_size, inner_size, coord_size); + status = gather(utils::castInputData(input_data), + utils::castInputData(position_data), + utils::castOutputData(output_data), axis_size, + batch_size, outer_size, inner_size, coord_size); } break; default: diff --git a/onert-micro/onert-micro/src/execute/kernels/Pad.cpp b/onert-micro/onert-micro/src/execute/kernels/Pad.cpp index 7967b9165d5..1f7f418ccf7 100644 --- a/onert-micro/onert-micro/src/execute/kernels/Pad.cpp +++ b/onert-micro/onert-micro/src/execute/kernels/Pad.cpp @@ -113,6 +113,12 @@ OMStatus execute_kernel_CirclePad(const OMExecuteArgs &execute_args) { pad_params.left_padding[idx] = paddings_data[idx * 2]; pad_params.right_padding[idx] = paddings_data[idx * 2 + 1]; + + // Bounds check: padding values must be non-negative + if (pad_params.left_padding[idx] < 0 || pad_params.right_padding[idx] < 0) + { + return IndexError; + } } switch (input1->type()) diff --git a/res/TensorFlowLiteRecipes/Sign_000/test.recipe b/res/TensorFlowLiteRecipes/Sign_000/test.recipe new file mode 100644 index 00000000000..6768efe4213 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Sign_000/test.recipe @@ -0,0 +1,17 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operation { + type: "Sign" + input: "ifm" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/res/TensorFlowLiteRecipes/Sign_000/test.reverse b/res/TensorFlowLiteRecipes/Sign_000/test.reverse new file mode 100644 index 00000000000..e69de29bb2d diff --git a/runtime/compute/ARMComputeEx/resolve_includes.py b/runtime/compute/ARMComputeEx/resolve_includes.py index f37c2a9574a..91372fc639b 100755 --- a/runtime/compute/ARMComputeEx/resolve_includes.py +++ b/runtime/compute/ARMComputeEx/resolve_includes.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + # Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/runtime/onert/backend/trix/DevContext.cc b/runtime/onert/backend/trix/DevContext.cc index 06e1e5bdb5d..0d9ff03f6b5 100644 --- a/runtime/onert/backend/trix/DevContext.cc +++ b/runtime/onert/backend/trix/DevContext.cc @@ -246,58 +246,13 @@ void DevContext::runOneBatch(uint32_t dev_num, ModelID model_id, input_buffers * } const auto &dev_handle = _dev_handles.at(dev_num); - int req_id; - if (auto error_code = createNPU_request(dev_handle, model_id_at_device, &req_id)) + auto ret = runNPU_model(dev_handle, model_id_at_device, NPU_INFER_BLOCKING, input_bufs, + output_bufs, NULL, NULL); + if (ret < 0) { - throw std::runtime_error("Unable to create NPU request with model id (" + - std::to_string(model_id_at_device) + ")" + - " error code : " + std::to_string(error_code)); - } - - if (auto error_code = - setNPU_requestData(dev_handle, req_id, input_bufs, in_info, output_bufs, out_info)) - { - removeNPU_request(dev_handle, req_id); - throw std::runtime_error("Unable to create NPU request for model id (" + - std::to_string(model_id_at_device) + ")" + - " error code : " + std::to_string(error_code)); - } - - // NOTE submitNPU_request is not thread-safe(?). It is rarely hanging(unresponsive). - // Ultimately, to solve this problem, we have to either use other thread-safe API or - // change submitNPU_request to be thread-safe, but both works take time. - // As a workaround, let's allow hanging thread. - // TODO Change submitNPU_request to be thread-safe or replaced with other thread-safe API - std::packaged_task task(submitNPU_request); - auto f = task.get_future(); - std::thread thread_submit_request(std::move(task), dev_handle, req_id); - auto status = f.wait_until(std::chrono::system_clock::now() + std::chrono::seconds(TIMEOUT_SEC)); - if (status == std::future_status::timeout) - { - // There is no way to terminate hanging submitNPU_request from the outside. - // If a hanging thread is detached, it will remain as a hanging thread. Even so, it's better - // than having the main thread hanging. - thread_submit_request.detach(); - - // TODO Enable removeNPU_request after resolving hanging. - // removeNPU_request(dev_handle, req_id); - throw std::runtime_error("The npu API \"submitNPU_request\" timeout"); - } - - auto error_code = f.get(); - thread_submit_request.join(); - if (error_code != 0) - { - removeNPU_request(dev_handle, req_id); - throw std::runtime_error("Unable to submit NPU request with req id (" + std::to_string(req_id) + - ")" + " error code : " + std::to_string(error_code)); - } - - if (auto error_code = removeNPU_request(dev_handle, req_id)) - { - throw std::runtime_error("Unable to remove NPU request with req id (" + std::to_string(req_id) + - ")" + " error code : " + std::to_string(error_code)); + throw std::runtime_error("Unable to run NPU model with model id (" + + std::to_string(model_id_at_device) + ")"); } } diff --git a/runtime/onert/backend/trix/ops/BulkPipelineLayer.cc b/runtime/onert/backend/trix/ops/BulkPipelineLayer.cc index 1d8b1a0f958..03a8a5ccb4d 100644 --- a/runtime/onert/backend/trix/ops/BulkPipelineLayer.cc +++ b/runtime/onert/backend/trix/ops/BulkPipelineLayer.cc @@ -44,6 +44,8 @@ void BulkPipelineLayer::configure(const std::vector &in config.model_paths = binary_path; config.device_id = 0; // default device id = 0 config.n_owner_models = 2; // Use 2 owner models for buffer sharing + config.n_inputs = inputs.size(); + config.n_outputs = outputs.size(); _pipeline_manager = std::make_unique(config); @@ -61,6 +63,7 @@ void BulkPipelineLayer::run() } catch (const std::exception &e) { + _pipeline_manager->shutdown(); std::cerr << "BulkPipelineLayer execution failed: " << e.what() << std::endl; throw; } diff --git a/runtime/onert/backend/trix/ops/BulkPipelineManager.cc b/runtime/onert/backend/trix/ops/BulkPipelineManager.cc index 92ec79eea01..8a3615585d5 100644 --- a/runtime/onert/backend/trix/ops/BulkPipelineManager.cc +++ b/runtime/onert/backend/trix/ops/BulkPipelineManager.cc @@ -43,6 +43,7 @@ bool BulkPipelineManager::initialize() try { createModels(); + verifyModels(); prepareModels(); linkModels(); @@ -59,11 +60,6 @@ bool BulkPipelineManager::initialize() void BulkPipelineManager::shutdown() { - if (!_initialized.load()) - { - return; - } - _initialized = false; // Wait until all executions are finished @@ -212,6 +208,19 @@ void BulkPipelineManager::linkModels() } } +void BulkPipelineManager::verifyModels() +{ + for (auto &model : _models) + { + if ((static_cast(model->metadata()->input_seg_num) != _config.n_inputs) || + (static_cast(model->metadata()->output_seg_num) != _config.n_outputs)) + { + throw std::runtime_error("Model " + model->modelPath() + + " has different number of inputs/outputs"); + } + } +} + void BulkPipelineManager::prepareModels() { for (auto &model : _models) diff --git a/runtime/onert/backend/trix/ops/BulkPipelineManager.h b/runtime/onert/backend/trix/ops/BulkPipelineManager.h index 7ee64a40168..6801b26bf97 100644 --- a/runtime/onert/backend/trix/ops/BulkPipelineManager.h +++ b/runtime/onert/backend/trix/ops/BulkPipelineManager.h @@ -38,6 +38,8 @@ class BulkPipelineManager std::vector model_paths; int device_id{0}; int n_owner_models{2}; // number of models that share the buffers + uint32_t n_inputs{1}; + uint32_t n_outputs{1}; }; public: @@ -59,6 +61,7 @@ class BulkPipelineManager void createModels(); void linkModels(); void prepareModels(); + void verifyModels(); private: PipelineConfig _config; diff --git a/runtime/onert/backend/trix/ops/BulkPipelineModel.cc b/runtime/onert/backend/trix/ops/BulkPipelineModel.cc index 5cabc1443d2..3ab52e9e9b1 100644 --- a/runtime/onert/backend/trix/ops/BulkPipelineModel.cc +++ b/runtime/onert/backend/trix/ops/BulkPipelineModel.cc @@ -79,11 +79,6 @@ bool BulkPipelineModel::prepare() void BulkPipelineModel::release() { - if (!_prepared.load()) - { - return; - } - // Cancel a asynchronous job if (_async_fill_future.valid()) { diff --git a/runtime/onert/backend/trix/ops/test/BulkPipelineManager.test.cc b/runtime/onert/backend/trix/ops/test/BulkPipelineManager.test.cc index 359a0df6ec6..25fecce95c5 100644 --- a/runtime/onert/backend/trix/ops/test/BulkPipelineManager.test.cc +++ b/runtime/onert/backend/trix/ops/test/BulkPipelineManager.test.cc @@ -29,12 +29,23 @@ class BulkPipelineManagerTest : public ::testing::Test { BulkPipelineManager::PipelineConfig config; config.device_id = 0; - config.model_paths.push_back("model_path"); + config.model_paths.push_back("model_path_0"); + config.model_paths.push_back("model_path_1"); + config.n_inputs = 0; + config.n_outputs = 0; manager = std::make_unique(config); // Reset all mock syscalls before each test MockSyscallsManager::getInstance().resetAll(); + MockSyscallsManager::getInstance().setFopenHook([](const char *path, const char *) -> FILE * { + if (strcmp(path, "model_path_0") == 0) + { + return (FILE *)1; + } + return (FILE *)2; + }); + MockSyscallsManager::getInstance().setFreadHook( [](void *ptr, size_t size, size_t, FILE *) -> int { if (size == NPUBIN_META_SIZE) @@ -43,6 +54,8 @@ class BulkPipelineManagerTest : public ::testing::Test meta->program_size = 1024; meta->weight_size = 1024; meta->size = 4096; + meta->input_seg_num = 0; + meta->output_seg_num = 0; } return 1; }); @@ -61,6 +74,7 @@ class BulkPipelineManagerTest : public ::testing::Test void TearDown() override {} std::unique_ptr manager; + const int nr_models = 2; }; TEST_F(BulkPipelineManagerTest, test_initilize) @@ -71,9 +85,17 @@ TEST_F(BulkPipelineManagerTest, test_initilize) TEST_F(BulkPipelineManagerTest, test_shutdown) { + int nr_fclose_calls = 0; EXPECT_TRUE(manager->initialize()); + // This hook will checking the number of fclose() calls + MockSyscallsManager::getInstance().setFcloseHook([&nr_fclose_calls](FILE *) -> int { + nr_fclose_calls++; + return 0; + }); manager->shutdown(); EXPECT_FALSE(manager->isInitialized()); + // fclose() should be called as the same number of models + EXPECT_EQ(nr_fclose_calls, nr_models); } TEST_F(BulkPipelineManagerTest, test_execute) @@ -83,3 +105,29 @@ TEST_F(BulkPipelineManagerTest, test_execute) std::vector outputs; EXPECT_NO_THROW(manager->execute(inputs, outputs)); } + +TEST_F(BulkPipelineManagerTest, test_verify_models) +{ + MockSyscallsManager::getInstance().clearFreadHook(); + MockSyscallsManager::getInstance().setFreadHook( + [](void *ptr, size_t size, size_t, FILE *fp) -> int { + if (size == NPUBIN_META_SIZE) + { + auto meta = reinterpret_cast(ptr); + meta->program_size = 1024; + meta->weight_size = 1024; + meta->size = 4096; + meta->input_seg_num = 0; + if (fp == (FILE *)1) + { + meta->output_seg_num = 1; + } + else + { + meta->output_seg_num = 0; + } + } + return 1; + }); + EXPECT_FALSE(manager->initialize()); +} diff --git a/runtime/tests/nnapi/nnapi_test_generator/android-10/cts_generator.py b/runtime/tests/nnapi/nnapi_test_generator/android-10/cts_generator.py index 70f1767de54..3b1e1eb6ecf 100755 --- a/runtime/tests/nnapi/nnapi_test_generator/android-10/cts_generator.py +++ b/runtime/tests/nnapi/nnapi_test_generator/android-10/cts_generator.py @@ -99,13 +99,14 @@ def NeedRegenerate(): def InitializeFiles(model_fd, example_fd, test_fd): fileHeader = "// clang-format off\n// Generated file (from: {spec_file}). Do not edit" testFileHeader = """\ -#include "../../TestGenerated.h"\n -namespace {spec_name} {{ +#include "../../TestGenerated.h" +namespace cts_gen_{spec_name} {{ // Generated {spec_name} test #include "{example_file}" // Generated model constructor #include "{model_file}" -}} // namespace {spec_name}\n""" +}} // namespace cts_gen_{spec_name} +""" # This regex is to remove prefix and get relative path for #include # Fix for onert: update path pathRegex = r".*(runtime/tests/nnapi/src/)" @@ -272,13 +273,13 @@ def DumpCtsExample(example, example_fd): def DumpCtsTest(example, test_fd): testTemplate = """\ TEST_F({test_case_name}, {test_name}) {{ - execute({namespace}::{create_model_name}, - {namespace}::{is_ignored_name}, - {namespace}::get_{examples_name}(){log_file});\n}}\n""" - # Fix for onert: Remove version check - #if example.model.version is not None: - #testTemplate += """\ -#TEST_AVAILABLE_SINCE({version}, {test_name}, {namespace}::{create_model_name})\n""" + execute(cts_gen_{namespace}::{create_model_name}, + cts_gen_{namespace}::{is_ignored_name}, + cts_gen_{namespace}::get_{examples_name}(){log_file});\n}}\n""" +# Fix for onert: Remove version check +# if example.model.version is not None: +# testTemplate += """\ +# TEST_AVAILABLE_SINCE({version}, {test_name}, {namespace}::{create_model_name})\n""" print(testTemplate.format( test_case_name="DynamicOutputShapeTest" if example.model.hasDynamicOutputShape \ else "GeneratedTests", diff --git a/runtime/tests/nnapi/nnapi_test_generator/android-10/dynamic_tensor.py b/runtime/tests/nnapi/nnapi_test_generator/android-10/dynamic_tensor.py index 416b4099eea..763715b1ca0 100755 --- a/runtime/tests/nnapi/nnapi_test_generator/android-10/dynamic_tensor.py +++ b/runtime/tests/nnapi/nnapi_test_generator/android-10/dynamic_tensor.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + # # Copyright (C) 2018 The Android Open Source Project # Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved diff --git a/runtime/tests/nnapi/nnapi_test_generator/android-10/vts_generator.py b/runtime/tests/nnapi/nnapi_test_generator/android-10/vts_generator.py index c9504d05b03..8d7188761bb 100755 --- a/runtime/tests/nnapi/nnapi_test_generator/android-10/vts_generator.py +++ b/runtime/tests/nnapi/nnapi_test_generator/android-10/vts_generator.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # # Copyright 2017, The Android Open Source Project +# Copyright 2026, 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. @@ -271,15 +272,17 @@ def generate_vts_test(example, test_file): testTemplate = """\ TEST_F({test_case_name}, {test_name}) {{ generated_tests::Execute(device, - {namespace}::{create_model_name}, - {namespace}::{is_ignored_name}, - {namespace}::get_{examples_name}(){test_dynamic_output_shape});\n}} + vts_gen_{namespace}::{create_model_name}, + vts_gen_{namespace}::{is_ignored_name}, + vts_gen_{namespace}::get_{examples_name}(){test_dynamic_output_shape}); +}} TEST_F(ValidationTest, {test_name}) {{ - const Model model = {namespace}::{create_model_name}(); - const std::vector requests = createRequests({namespace}::get_{examples_name}()); + const Model model = vts_gen_{namespace}::{create_model_name}(); + const std::vector requests = createRequests(vts_gen_{namespace}::get_{examples_name}()); validateEverything(model, requests); -}}\n +}} + """ if example.model.hasDynamicOutputShape: print("#ifdef NN_TEST_DYNAMIC_OUTPUT_SHAPE", file=test_fd) @@ -300,12 +303,12 @@ def InitializeFiles(model_fd, example_fd, test_fd): fileHeader = "// clang-format off\n// Generated file (from: {spec_file}). Do not edit" testFileHeader = """\ // Generated from: {spec_file}. -namespace {spec_name} {{ +namespace vts_gen_{spec_name} {{ // Generated {spec_name} test #include "{example_file}" // Generated model constructor #include "{model_file}" -}} // namespace {spec_name}\n""" +}} // namespace vts_gen_{spec_name}\n""" # This regex is to remove prefix and get relative path for #include pathRegex = r".*frameworks/ml/nn/(runtime/test/generated/)?" specFileBase = os.path.basename(tg.FileNames.specFile) diff --git a/runtime/tests/nnapi/specs/generate_vts_test.sh b/runtime/tests/nnapi/specs/generate_vts_test.sh index 5561f7043de..981fe9284b3 100755 --- a/runtime/tests/nnapi/specs/generate_vts_test.sh +++ b/runtime/tests/nnapi/specs/generate_vts_test.sh @@ -1,5 +1,8 @@ +#!/usr/bin/env bash + # # Copyright (C) 2017 The Android Open Source Project +# Copyright (C) 2026 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. @@ -29,20 +32,20 @@ function generate_one_testcase { -e $VTS_PATH/generated/examples/$BASENAME.example.cpp # Paste these lines into TestGenerated.cpp echo - echo namespace $BASENAME { - echo std::vector\ examples \= { - echo // Generated $BASENAME test - echo \#include \"examples/$BASENAME.example.cpp\" - echo }\; - echo // Generated model constructor - echo \#include \"vts_models/$BASENAME.model.cpp\" - echo } // namespace $BASENAME - echo TEST_F\(NeuralnetworksHidlTest\, $BASENAME\) { - echo ' generated_tests::Execute'\(device, - echo ' '$BASENAME\:\:createTestModel\, - echo ' '$BASENAME\:\:is_ignored\, - echo ' '$BASENAME\:\:examples\)\; - echo } + echo "namespace vts_gen_$BASENAME {" + echo "std::vector examples = {" + echo "// Generated $BASENAME test" + echo "#include \"examples/$BASENAME.example.cpp\"" + echo "};" + echo "// Generated model constructor" + echo "#include \"vts_models/$BASENAME.model.cpp\"" + echo "} // namespace vts_gen_$BASENAME" + echo "TEST_F(NeuralnetworksHidlTest, $BASENAME) {" + echo " generated_tests::Execute(device," + echo " vts_gen_$BASENAME::createTestModel," + echo " vts_gen_$BASENAME::is_ignored," + echo " vts_gen_$BASENAME::examples);" + echo "}" } for ver in $NNAPI_VERSION; diff --git a/tools/pbfile_tool/convert_ckpt_to_pb.py b/tools/pbfile_tool/convert_ckpt_to_pb.py index cd43143ca07..45c3c2bf763 100755 --- a/tools/pbfile_tool/convert_ckpt_to_pb.py +++ b/tools/pbfile_tool/convert_ckpt_to_pb.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + # Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/tools/pbfile_tool/convert_pb_to_pbtxt.py b/tools/pbfile_tool/convert_pb_to_pbtxt.py index 28a3da4c275..6dde2ea9bcf 100755 --- a/tools/pbfile_tool/convert_pb_to_pbtxt.py +++ b/tools/pbfile_tool/convert_pb_to_pbtxt.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + # Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/tools/pbfile_tool/extract_subgraph.py b/tools/pbfile_tool/extract_subgraph.py index b04d91c77ae..d3655176136 100755 --- a/tools/pbfile_tool/extract_subgraph.py +++ b/tools/pbfile_tool/extract_subgraph.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + import tensorflow as tf import argparse diff --git a/tools/tensorflow_model_freezer/base_freezer.py b/tools/tensorflow_model_freezer/base_freezer.py index 788976e8110..6d722e80987 100755 --- a/tools/tensorflow_model_freezer/base_freezer.py +++ b/tools/tensorflow_model_freezer/base_freezer.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + # Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/tools/tensorflow_model_freezer/model_freezer_util.py b/tools/tensorflow_model_freezer/model_freezer_util.py index 7494588e2c0..9c87d8b06b8 100755 --- a/tools/tensorflow_model_freezer/model_freezer_util.py +++ b/tools/tensorflow_model_freezer/model_freezer_util.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + # Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/tools/tflkit/summarize_pb.py b/tools/tflkit/summarize_pb.py index cadaa3c3ad1..59d9c4e9023 100755 --- a/tools/tflkit/summarize_pb.py +++ b/tools/tflkit/summarize_pb.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + import argparse import os import subprocess