From 85ab2a3ba64e0820d82efd76b1d420dc086c546e Mon Sep 17 00:00:00 2001 From: Chris Sidebottom Date: Fri, 5 Aug 2022 16:26:38 +0100 Subject: [PATCH 1/5] [Target] Add Target Parser for Arm(R) Cortex(R) A-Profile CPUs This implements an initial Target Parser which aims to consolidate architecture feature detection from a few different places: * https://github.com/apache/tvm/blob/d2db9cb0d839e32778f461b77e59f6418282a511/python/tvm/topi/arm_cpu/arm_utils.py#L24-L70 * https://github.com/apache/tvm/blob/02fbaf0ed9120a8f95155e63de42459f230584aa/python/tvm/relay/qnn/op/legalizations.py#L350-L359 * https://github.com/apache/tvm/blob/b542724873140bb051492530d97a78b9b7b7983d/python/tvm/relay/op/strategy/arm_cpu.py#L232 A further patch will remove all of the above and replace usages with the `.features` map. --- src/target/parsers/aprofile.cc | 165 ++++++++++++ src/target/parsers/aprofile.h | 43 ++++ tests/cpp/target/parsers/aprofile_test.cc | 291 ++++++++++++++++++++++ 3 files changed, 499 insertions(+) create mode 100644 src/target/parsers/aprofile.cc create mode 100644 src/target/parsers/aprofile.h create mode 100644 tests/cpp/target/parsers/aprofile_test.cc diff --git a/src/target/parsers/aprofile.cc b/src/target/parsers/aprofile.cc new file mode 100644 index 000000000000..feabd473b5ab --- /dev/null +++ b/src/target/parsers/aprofile.cc @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/*! + * \file tvm/target/parsers/aprofile.cc + * \brief Target Parser for Arm(R) Cortex(R) A-Profile CPUs + */ + +#include "aprofile.h" + +#include +#include + +#include "../../support/utils.h" + +namespace tvm { +namespace target { +namespace parsers { +namespace aprofile { + +const std::regex version_regex("\\+v(\\d+\\.\\d+)a"); + +static inline double GetArchVersion(Array mattr) { + std::smatch version; + + for (const String& attr : mattr) { + std::string attr_string = attr; + if (std::regex_match(attr_string, version, version_regex)) { + if (version.size() == 2) { + return atof(version[1].str().data()); + } + } + } + return 0.0; +} + +static inline double GetArchVersion(Optional> attr) { + if (!attr) { + return false; + } + return GetArchVersion(attr.value()); +} + +static inline bool HasFlag(String attr, std::string flag) { + std::string attr_str = attr; + return attr_str.find(flag) != std::string::npos; +} + +static inline bool HasFlag(Optional attr, std::string flag) { + if (!attr) { + return false; + } + return HasFlag(attr.value(), flag); +} + +static inline bool HasFlag(Optional> attr, std::string flag) { + if (!attr) { + return false; + } + Array attr_array = attr.value(); + + auto matching_attr = std::find_if(attr_array.begin(), attr_array.end(), + [flag](String attr_str) { return HasFlag(attr_str, flag); }); + return matching_attr != attr_array.end(); +} + +static bool HasFlag(Optional mcpu, Optional> mattr, std::string flag) { + return HasFlag(mcpu, flag) || HasFlag(mattr, flag); +} + +bool IsAArch32(Optional mtriple, Optional mcpu) { + if (mtriple) { + bool is_mprofile = mcpu && support::StartsWith(mcpu.value(), "cortex-m"); + return support::StartsWith(mtriple.value(), "arm") && !is_mprofile; + } + return false; +} + +bool IsAArch64(Optional mtriple) { + if (mtriple) { + return support::StartsWith(mtriple.value(), "aarch64"); + } + return false; +} + +bool IsArch(TargetJSON attrs) { + Optional mtriple = Downcast>(attrs.Get("mtriple")); + Optional mcpu = Downcast>(attrs.Get("mcpu")); + + return IsAArch32(mtriple, mcpu) || IsAArch64(mtriple); +} + +static TargetFeatures GetFeatures(TargetJSON target) { + Optional mcpu = Downcast>(target.Get("mcpu")); + Optional mtriple = Downcast>(target.Get("mtriple")); + Optional> mattr = Downcast>>(target.Get("mattr")); + + double arch_version = GetArchVersion(mattr); + + bool is_aarch64 = IsAArch64(mtriple); + + bool simd_flag = HasFlag(mcpu, mattr, "+neon") || HasFlag(mcpu, mattr, "+simd"); + bool has_asimd = is_aarch64 || simd_flag; + + bool i8mm_flag = HasFlag(mcpu, mattr, "+i8mm"); + bool i8mm_disable = HasFlag(mcpu, mattr, "+noi8mm"); + bool i8mm_default = arch_version >= 8.6; + bool i8mm_support = arch_version >= 8.2 && arch_version <= 8.5; + bool has_i8mm = (i8mm_default && !i8mm_disable) || (i8mm_support && i8mm_flag); + + bool dotprod_flag = HasFlag(mcpu, mattr, "+dotprod"); + bool dotprod_disable = HasFlag(mcpu, mattr, "+nodotprod"); + bool dotprod_default = arch_version >= 8.4; + bool dotprod_support = arch_version >= 8.2 && arch_version <= 8.3; + bool has_dotprod = (dotprod_default && !dotprod_disable) || (dotprod_support && dotprod_flag); + + return { + {"is_aarch64", Bool(is_aarch64)}, + {"has_asimd", Bool(has_asimd)}, + {"has_dotprod", Bool(has_dotprod)}, + {"has_matmul_i8", Bool(has_i8mm)}, + }; +} + +static Array MergeKeys(Optional> existing_keys) { + const String kExtraKey = "arm_cpu"; + + if (!existing_keys) { + return {kExtraKey}; + } + + Array keys = existing_keys.value(); + if (std::find(keys.begin(), keys.end(), kExtraKey) == keys.end()) { + keys.push_back(kExtraKey); + } + return keys; +} + +TargetJSON ParseTarget(TargetJSON target) { + target.Set("features", GetFeatures(target)); + target.Set("keys", MergeKeys(Downcast>>(target.Get("keys")))); + + return target; +} + +} // namespace aprofile +} // namespace parsers +} // namespace target +} // namespace tvm diff --git a/src/target/parsers/aprofile.h b/src/target/parsers/aprofile.h new file mode 100644 index 000000000000..7ded9ac5456a --- /dev/null +++ b/src/target/parsers/aprofile.h @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/*! + * \file tvm/target/parsers/aprofile.h + * \brief Target Parser for Arm(R) Cortex(R) A-Profile CPUs + */ + +#ifndef TVM_TARGET_PARSERS_APROFILE_H_ +#define TVM_TARGET_PARSERS_APROFILE_H_ + +#include + +namespace tvm { +namespace target { +namespace parsers { +namespace aprofile { + +bool IsArch(TargetJSON target); +TargetJSON ParseTarget(TargetJSON target); + +} // namespace aprofile +} // namespace parsers +} // namespace target +} // namespace tvm + +#endif // TVM_TARGET_PARSERS_APROFILE_H_ diff --git a/tests/cpp/target/parsers/aprofile_test.cc b/tests/cpp/target/parsers/aprofile_test.cc new file mode 100644 index 000000000000..f08fc758ec71 --- /dev/null +++ b/tests/cpp/target/parsers/aprofile_test.cc @@ -0,0 +1,291 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "../src/target/parsers/aprofile.h" + +#include + +#include +#include + +namespace tvm { +namespace target { +namespace parsers { +namespace aprofile { + +static float defaultI8MM = 8.6; +static float optionalI8MM[] = {8.2, 8.3, 8.4, 8.5}; +static float defaultDotProd = 8.4; +static float optionalDotProd[] = {8.2, 8.3}; + +class AProfileOptionalI8MM : public testing::TestWithParam {}; +class AProfileOptionalDotProd : public testing::TestWithParam {}; + +static TargetFeatures ParseTargetWithAttrs(String mcpu, String mtriple, Array mattr) { + return ParseTarget({ + {"mcpu", mcpu}, + {"mtriple", mtriple}, + {"mattr", mattr}, + }); +} + +TEST(AProfileParser, ParseTargetKeys) { + TargetJSON target = ParseTarget({}); + Array keys = Downcast>(target.at("keys")); + ASSERT_EQ(keys.size(), 1); + ASSERT_EQ(keys[0], "arm_cpu"); +} + +TEST(AProfileParser, ParseTargetWithExistingKeys) { + TargetJSON target = ParseTarget({ + {"keys", Array{"cpu"}}, + }); + TargetFeatures features = Downcast(target.at("features")); + Array keys = Downcast>(target.at("keys")); + ASSERT_EQ(keys.size(), 2); + ASSERT_EQ(keys[0], "cpu"); + ASSERT_EQ(keys[1], "arm_cpu"); +} + +TEST(AProfileParser, ParseTargetWithDuplicateKey) { + TargetJSON target = ParseTarget({ + {"keys", Array{"cpu", "arm_cpu"}}, + }); + TargetFeatures features = Downcast(target.at("features")); + Array keys = Downcast>(target.at("keys")); + ASSERT_EQ(keys.size(), 2); + ASSERT_EQ(keys[0], "cpu"); + ASSERT_EQ(keys[1], "arm_cpu"); +} + +TEST(AProfileParser, ParseTargetDefaults) { + TargetJSON target = ParseTarget({}); + TargetFeatures features = Downcast(target.at("features")); + + ASSERT_EQ(Downcast(features.at("is_aarch64")), false); + ASSERT_EQ(Downcast(features.at("has_asimd")), false); + ASSERT_EQ(Downcast(features.at("has_dotprod")), false); + ASSERT_EQ(Downcast(features.at("has_matmul_i8")), false); +} + +TEST(AProfileParser, IsAArch64Triple) { + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {""}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("is_aarch64")), true); +} + +TEST(AProfileParser, IsAArch32Triple) { + TargetJSON target = ParseTargetWithAttrs("", "armv7a-arm-none-eabi", {""}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("is_aarch64")), false); + + target = ParseTargetWithAttrs("", "armv8a-arm-none-eabi", {""}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("is_aarch64")), false); + + target = ParseTargetWithAttrs("", "arm-unknown-linux-gnu", {""}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("is_aarch64")), false); +} + +TEST(AProfileParser, IsAArch32BlankCPU) { + TargetJSON target = ParseTarget({ + {"mtriple", String("arm-unknown-linux-gnu")}, + }); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); +} + +TEST(AProfileParser, IsAArch32TripleWithAProfile) { + TargetJSON target = ParseTargetWithAttrs("cortex-a53", "armv7a-arm-none-eabi", {""}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("is_aarch64")), false); + + target = ParseTargetWithAttrs("cortex-a53", "armv8a-arm-none-eabi", {""}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("is_aarch64")), false); + + target = ParseTargetWithAttrs("cortex-a53", "arm-unknown-linux-gnu", {""}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("is_aarch64")), false); +} + +TEST(AProfileParser, IsAArch32TripleWithMProfile) { + TargetJSON target = ParseTargetWithAttrs("cortex-m33", "armv7a-arm-none-eabi", {""}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), false); + + target = ParseTargetWithAttrs("cortex-m33", "armv8a-arm-none-eabi", {""}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), false); + + target = ParseTargetWithAttrs("cortex-m33", "arm-unknown-linux-gnu", {""}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), false); +} + +TEST(AProfileParser, AArch64HasASIMD) { + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {""}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_asimd")), true); +} + +TEST(AProfileParser, AArch32NoASIMD) { + TargetJSON target = ParseTargetWithAttrs("", "armv8a-arm-none-eabi", {}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_asimd")), false); +} + +TEST(AProfileParser, AArch32HasASIMDWithOption) { + TargetJSON target = ParseTargetWithAttrs("", "armv8a-arm-none-eabi", {"+simd"}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_asimd")), true); + + target = ParseTargetWithAttrs("cortex-a+simd", "armv8a-arm-none-eabi", {""}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_asimd")), true); +} + +TEST(AProfileParser, AArch32HasASIMDWithAlternativeOption) { + TargetJSON target = ParseTargetWithAttrs("", "armv8a-arm-none-eabi", {"+neon"}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_asimd")), true); + + target = ParseTargetWithAttrs("cortex-a+neon", "armv8a-arm-none-eabi", {""}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_asimd")), true); +} + +TEST(AProfileParser, NoI8MMSupport) { + std::string attr = "+v8.0a"; + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {attr, "+i8mm"}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_matmul_i8")), false); +} + +TEST(AProfileParser, DefaultI8MMSupport) { + std::string arch_attr = "+v" + std::to_string(defaultI8MM) + "a"; + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_matmul_i8")), true); +} + +TEST(AProfileParser, DefaultI8MMSupportDisable) { + std::string arch_attr = "+v" + std::to_string(defaultI8MM) + "a"; + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr, "+noi8mm"}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_matmul_i8")), false); + + target = ParseTargetWithAttrs("cortex-a+noi8mm", "aarch64-arm-none-eabi", {arch_attr}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_matmul_i8")), false); +} + +TEST_P(AProfileOptionalI8MM, OptionalI8MMSupport) { + std::string arch_attr = "+v" + std::to_string(GetParam()) + "a"; + + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_matmul_i8")), false); + + target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr, "+i8mm"}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_matmul_i8")), true); + + target = ParseTargetWithAttrs("cortex-a+i8mm", "aarch64-arm-none-eabi", {arch_attr}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_matmul_i8")), true); +} + +TEST(AProfileParser, NoDotProdSupport) { + std::string attr = "+v8.0a"; + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {attr, "+dotprod"}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_dotprod")), false); +} + +TEST(AProfileParser, DefaultDotProdSupport) { + std::string arch_attr = "+v" + std::to_string(defaultDotProd) + "a"; + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_dotprod")), true); +} + +TEST(AProfileParser, DefaultDotProdSupportDisable) { + std::string arch_attr = "+v" + std::to_string(defaultDotProd) + "a"; + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr, "+nodotprod"}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_dotprod")), false); + + target = ParseTargetWithAttrs("cortex-a+nodotprod", "aarch64-arm-none-eabi", {arch_attr}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_dotprod")), false); +} + +TEST_P(AProfileOptionalDotProd, OptionalDotProdSupport) { + std::string arch_attr = "+v" + std::to_string(GetParam()) + "a"; + + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_dotprod")), false); + + target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr, "+dotprod"}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_dotprod")), true); + + target = ParseTargetWithAttrs("cortex-a+dotprod", "aarch64-arm-none-eabi", {arch_attr}); + features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_dotprod")), true); +} + +INSTANTIATE_TEST_CASE_P(AProfileParser, AProfileOptionalI8MM, ::testing::ValuesIn(optionalI8MM)); +INSTANTIATE_TEST_CASE_P(AProfileParser, AProfileOptionalDotProd, + ::testing::ValuesIn(optionalDotProd)); + +} // namespace aprofile +} // namespace parsers +} // namespace target +} // namespace tvm From bb60f879340db330ff6b9d3475d59a35d6e2c236 Mon Sep 17 00:00:00 2001 From: Chris Sidebottom Date: Mon, 17 Oct 2022 15:35:16 +0100 Subject: [PATCH 2/5] Move regex into version parser --- src/target/parsers/aprofile.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/target/parsers/aprofile.cc b/src/target/parsers/aprofile.cc index feabd473b5ab..caa20afcf7f7 100644 --- a/src/target/parsers/aprofile.cc +++ b/src/target/parsers/aprofile.cc @@ -34,9 +34,8 @@ namespace target { namespace parsers { namespace aprofile { -const std::regex version_regex("\\+v(\\d+\\.\\d+)a"); - static inline double GetArchVersion(Array mattr) { + std::regex version_regex("\\+v(\\d+\\.\\d+)a"); std::smatch version; for (const String& attr : mattr) { From 7063804b9ba1b54b0b6f45e0ab8062fcd179057a Mon Sep 17 00:00:00 2001 From: Chris Sidebottom Date: Mon, 17 Oct 2022 17:00:00 +0100 Subject: [PATCH 3/5] Un-inline some functions to see if that fixes pytorch --- src/target/parsers/aprofile.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/parsers/aprofile.cc b/src/target/parsers/aprofile.cc index caa20afcf7f7..cab829b513d8 100644 --- a/src/target/parsers/aprofile.cc +++ b/src/target/parsers/aprofile.cc @@ -34,7 +34,7 @@ namespace target { namespace parsers { namespace aprofile { -static inline double GetArchVersion(Array mattr) { +static double GetArchVersion(Array mattr) { std::regex version_regex("\\+v(\\d+\\.\\d+)a"); std::smatch version; @@ -49,7 +49,7 @@ static inline double GetArchVersion(Array mattr) { return 0.0; } -static inline double GetArchVersion(Optional> attr) { +static double GetArchVersion(Optional> attr) { if (!attr) { return false; } From 9384958e709b82a0efb84a6f672bb1d3d904c312 Mon Sep 17 00:00:00 2001 From: Chris Sidebottom Date: Wed, 19 Oct 2022 14:43:35 +0100 Subject: [PATCH 4/5] Un-static ArchVersion functions --- src/target/parsers/aprofile.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/parsers/aprofile.cc b/src/target/parsers/aprofile.cc index cab829b513d8..7808ffbef32d 100644 --- a/src/target/parsers/aprofile.cc +++ b/src/target/parsers/aprofile.cc @@ -34,7 +34,7 @@ namespace target { namespace parsers { namespace aprofile { -static double GetArchVersion(Array mattr) { +double GetArchVersion(Array mattr) { std::regex version_regex("\\+v(\\d+\\.\\d+)a"); std::smatch version; @@ -49,7 +49,7 @@ static double GetArchVersion(Array mattr) { return 0.0; } -static double GetArchVersion(Optional> attr) { +double GetArchVersion(Optional> attr) { if (!attr) { return false; } From c566c978978b50325a6cc2d18959672e4fa17288 Mon Sep 17 00:00:00 2001 From: Chris Sidebottom Date: Wed, 19 Oct 2022 19:50:03 +0100 Subject: [PATCH 5/5] Replace regex with string logic --- src/target/parsers/aprofile.cc | 12 ++++-------- tests/cpp/target/parsers/aprofile_test.cc | 8 ++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/target/parsers/aprofile.cc b/src/target/parsers/aprofile.cc index 7808ffbef32d..2fd5fe71e617 100644 --- a/src/target/parsers/aprofile.cc +++ b/src/target/parsers/aprofile.cc @@ -24,7 +24,6 @@ #include "aprofile.h" -#include #include #include "../../support/utils.h" @@ -35,15 +34,12 @@ namespace parsers { namespace aprofile { double GetArchVersion(Array mattr) { - std::regex version_regex("\\+v(\\d+\\.\\d+)a"); - std::smatch version; - for (const String& attr : mattr) { std::string attr_string = attr; - if (std::regex_match(attr_string, version, version_regex)) { - if (version.size() == 2) { - return atof(version[1].str().data()); - } + size_t attr_len = attr_string.size(); + if (attr_len >= 4 && attr_string.substr(0, 2) == "+v" && attr_string.back() == 'a') { + std::string version_string = attr_string.substr(2, attr_string.size() - 2); + return atof(version_string.data()); } } return 0.0; diff --git a/tests/cpp/target/parsers/aprofile_test.cc b/tests/cpp/target/parsers/aprofile_test.cc index f08fc758ec71..0382e7a84bd7 100644 --- a/tests/cpp/target/parsers/aprofile_test.cc +++ b/tests/cpp/target/parsers/aprofile_test.cc @@ -281,6 +281,14 @@ TEST_P(AProfileOptionalDotProd, OptionalDotProdSupport) { ASSERT_EQ(Downcast(features.at("has_dotprod")), true); } +TEST(AProfileParser, ArchVersionInvalidLetter) { + std::string arch_attr = "+v" + std::to_string(defaultDotProd) + "b"; + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); + TargetFeatures features = Downcast(target.at("features")); + ASSERT_EQ(IsArch(target), true); + ASSERT_EQ(Downcast(features.at("has_dotprod")), false); +} + INSTANTIATE_TEST_CASE_P(AProfileParser, AProfileOptionalI8MM, ::testing::ValuesIn(optionalI8MM)); INSTANTIATE_TEST_CASE_P(AProfileParser, AProfileOptionalDotProd, ::testing::ValuesIn(optionalDotProd));