From dfcdb3ec75a695db6f5cec78884f42058a4f68b4 Mon Sep 17 00:00:00 2001 From: Luke Hutton Date: Mon, 15 Jan 2024 15:10:37 +0000 Subject: [PATCH] [Target] Use LLVM target parser for determining Arm(R) A-Profile Architecture features Currently, target features are determined by a set of fixed checks on the target string. This works well for checking support of a small number of simple features, but it doesn't scale. Some problems include: - There are many non-trivial conditions for which a feature may(not) be available. It is easy to miss these with the current implementation. - The inclusion of some features in a target string can imply other features. For example, "+sve" implies "+neon". This currently isn't taken into account. - The tests in tests/cpp/target/parsers/aprofile_test.c suggest that targets such as "llvm -mcpu=cortex-a+neon" and "llvm -mattr=+noneon" are supported target strings. The features will be correctly parsed in TVM, however, they are not valid in LLVM. Therefore, it's possible that TVM and LLVM have different understanding of the features available. This commit uses the more robust LLVM target parser to determine support for the features in TVM. It leverages previous infrastructure added to TVM for obtaining a list of all supported features given an input target, and uses this to check the existance of certain features we're interested in. It should be trivial to grow this list over time. As a result of this change, the problems mentioned above are solved. In the current form, this commit drops support for target strings such as "llvm -mcpu=cortex-a+neon" and "llvm -mattr=+noneon". A scan of the codebase suggests this functionality is not in use (only in test cases). Should we feel the need to support them, or have a smoother migration for downstream users of TVM we can add a translator to the parser to convert these into LLVM compatible targets. Change-Id: Ic2bf3b68c8af74025ec388d304bd014624c0c585 --- src/target/llvm/llvm_instance.cc | 37 ++-- src/target/llvm/llvm_instance.h | 13 +- src/target/llvm/llvm_module.cc | 7 +- src/target/parsers/aprofile.cc | 72 ++------ tests/cpp/target/parsers/aprofile_test.cc | 166 ++++++++++-------- .../strategy/test_select_implementation.py | 2 +- 6 files changed, 148 insertions(+), 149 deletions(-) diff --git a/src/target/llvm/llvm_instance.cc b/src/target/llvm/llvm_instance.cc index 08ba34cc73fad..1a28383e02afb 100644 --- a/src/target/llvm/llvm_instance.cc +++ b/src/target/llvm/llvm_instance.cc @@ -199,21 +199,23 @@ std::ostream& operator<<(std::ostream& os, const LLVMTargetInfo::Option& opt) { return os; } -LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) { - triple_ = target->GetAttr("mtriple").value_or("default"); +LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) + : LLVMTargetInfo(instance, target->Export()) {} +LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const TargetJSON& target) { + triple_ = Downcast(target.Get("mtriple").value_or(String("default"))); if (triple_.empty() || triple_ == "default") { triple_ = llvm::sys::getDefaultTargetTriple(); } - cpu_ = target->GetAttr("mcpu").value_or(defaults::cpu); + cpu_ = Downcast(target.Get("mcpu").value_or(String(defaults::cpu))); - if (const Optional>& v = target->GetAttr>("mattr")) { + if (const auto& v = Downcast>>(target.Get("mattr"))) { for (const String& s : v.value()) { attrs_.push_back(s); } } // llvm module target - if (target->kind->name == "llvm") { + if (Downcast(target.Get("kind")) == "llvm") { // legalize -mcpu with the target -mtriple auto arches = GetAllLLVMTargetArches(); bool has_arch = @@ -224,7 +226,7 @@ LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) { } } - if (const Optional>& v = target->GetAttr>("cl-opt")) { + if (const auto& v = Downcast>>(target.Get("cl-opt"))) { llvm::StringMap& options = llvm::cl::getRegisteredOptions(); bool parse_error = false; for (const String& s : v.value()) { @@ -245,7 +247,7 @@ LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) { } llvm::FloatABI::ABIType float_abi = llvm::FloatABI::Default; - if (const Optional& v = target->GetAttr("mfloat-abi")) { + if (const auto& v = Downcast>(target.Get("mfloat-abi"))) { String value = v.value(); if (value == "hard") { float_abi = llvm::FloatABI::Hard; @@ -268,14 +270,14 @@ LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) { target_options_.NoInfsFPMath = false; target_options_.NoNaNsFPMath = true; target_options_.FloatABIType = float_abi; - if (const Optional& v = target->GetAttr("mabi")) { - target_options_.MCOptions.ABIName = v.value(); + if (target.find("mabi") != target.end()) { + target_options_.MCOptions.ABIName = Downcast(target.Get("mabi")); } - auto maybe_level = target->GetAttr("opt-level"); + auto maybe_level = Downcast(target.Get("opt-level")); #if TVM_LLVM_VERSION <= 170 if (maybe_level.defined()) { - int level = maybe_level.value()->value; + int level = maybe_level->value; if (level <= 0) { opt_level_ = llvm::CodeGenOpt::None; } else if (level == 1) { @@ -312,7 +314,7 @@ LLVMTargetInfo::LLVMTargetInfo(LLVMInstance& instance, const Target& target) { // Fast math options auto GetBoolFlag = [&target](llvm::StringRef flag) -> bool { - return target->GetAttr(flag.str()).value_or(Bool(false)); + return Downcast(target.Get(flag.str()).value_or(Bool(false))); }; if (GetBoolFlag("fast-math")) { #if TVM_LLVM_VERSION >= 60 @@ -831,7 +833,7 @@ const Array LLVMTargetInfo::GetAllLLVMTargetArches() const { return cpu_arches; } -const Array LLVMTargetInfo::GetAllLLVMCpuFeatures() const { +const Map LLVMTargetInfo::GetAllLLVMCpuFeatures() const { std::string feats = ""; for (const auto& attr : attrs_) { feats += feats.empty() ? attr : ("," + attr); @@ -845,10 +847,11 @@ const Array LLVMTargetInfo::GetAllLLVMCpuFeatures() const { #else MCInfo->getAllProcessorFeatures(); #endif - Array cpu_features; + // TVM doesn't have an FFI friendly Set, so use a Map instead for now + Map cpu_features; for (const auto& feat : llvm_features) { if (MCInfo->checkFeatures("+" + std::string(feat.Key))) { - cpu_features.push_back(feat.Key); + cpu_features.Set(feat.Key, ""); } } @@ -858,9 +861,7 @@ const Array LLVMTargetInfo::GetAllLLVMCpuFeatures() const { const bool LLVMTargetInfo::TargetHasCPUFeature(const std::string& feature) const { // lookup features for `-mcpu` auto feats = GetAllLLVMCpuFeatures(); - bool has_feature = - std::any_of(feats.begin(), feats.end(), [&](const auto& var) { return var == feature; }); - + bool has_feature = feats.find(feature) != feats.end(); return has_feature; } diff --git a/src/target/llvm/llvm_instance.h b/src/target/llvm/llvm_instance.h index 030a7db7210f3..209208bf8872b 100644 --- a/src/target/llvm/llvm_instance.h +++ b/src/target/llvm/llvm_instance.h @@ -156,6 +156,14 @@ class LLVMTargetInfo { */ // NOLINTNEXTLINE(runtime/references) LLVMTargetInfo(LLVMInstance& scope, const std::string& target_str); + /*! + * \brief Constructs LLVMTargetInfo from `Target` + * \param scope LLVMInstance object + * \param target TVM JSON Target object for target "llvm" + */ + // NOLINTNEXTLINE(runtime/references) + LLVMTargetInfo(LLVMInstance& instance, const TargetJSON& target); + /*! * \brief Destroys LLVMTargetInfo object */ @@ -285,11 +293,12 @@ class LLVMTargetInfo { /*! * \brief Get all CPU features from target - * \return list with all valid cpu features + * \return Map with all valid cpu features as keys and empty string as value. The Map + * is intended to be used as a Set, which TVM does not currently support. * \note The features are fetched from the LLVM backend using the target `-mtriple` * and the `-mcpu` architecture, but also consider the `-mattr` attributes. */ - const Array GetAllLLVMCpuFeatures() const; + const Map GetAllLLVMCpuFeatures() const; /*! * \brief Check the target if has a specific cpu feature diff --git a/src/target/llvm/llvm_module.cc b/src/target/llvm/llvm_module.cc index 62ea797edd2ef..0f52c6cc36293 100644 --- a/src/target/llvm/llvm_module.cc +++ b/src/target/llvm/llvm_module.cc @@ -545,12 +545,12 @@ TVM_REGISTER_GLOBAL("target.llvm_get_cpu_archlist") }); TVM_REGISTER_GLOBAL("target.llvm_get_cpu_features") - .set_body_typed([](const Target& target) -> Array { + .set_body_typed([](const Target& target) -> Map { auto use_target = target.defined() ? target : Target::Current(false); // ignore non "llvm" target if (target.defined()) { if (target->kind->name != "llvm") { - return Array{}; + return {}; } } auto llvm_instance = std::make_unique(); @@ -570,8 +570,7 @@ TVM_REGISTER_GLOBAL("target.llvm_cpu_has_feature") auto llvm_instance = std::make_unique(); LLVMTargetInfo llvm_backend(*llvm_instance, use_target); auto cpu_features = llvm_backend.GetAllLLVMCpuFeatures(); - bool has_feature = std::any_of(cpu_features.begin(), cpu_features.end(), - [&](auto& var) { return var == feature; }); + bool has_feature = cpu_features.find(feature) != cpu_features.end(); return has_feature; }); diff --git a/src/target/parsers/aprofile.cc b/src/target/parsers/aprofile.cc index 622ec5cc3fbf1..cc1dc4852388a 100644 --- a/src/target/parsers/aprofile.cc +++ b/src/target/parsers/aprofile.cc @@ -24,9 +24,11 @@ #include "aprofile.h" +#include #include #include "../../support/utils.h" +#include "../llvm/llvm_instance.h" namespace tvm { namespace target { @@ -52,33 +54,6 @@ double GetArchVersion(Optional> attr) { 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"); @@ -102,38 +77,25 @@ bool IsArch(TargetJSON attrs) { } static TargetFeatures GetFeatures(TargetJSON target) { - Optional mcpu = Downcast>(target.Get("mcpu")); - Optional mtriple = Downcast>(target.Get("mtriple")); - Optional> mattr = Downcast>>(target.Get("mattr")); - - const double arch_version = GetArchVersion(mattr); - - const bool is_aarch64 = IsAArch64(mtriple); + String kind = Downcast(target.Get("kind")); + ICHECK_EQ(kind, "llvm") << "Expected target kind 'llvm', but got '" << kind << "'"; - const bool simd_flag = HasFlag(mcpu, mattr, "+neon") || HasFlag(mcpu, mattr, "+simd"); - const bool has_asimd = is_aarch64 || simd_flag; - const bool has_sve = HasFlag(mcpu, mattr, "+sve"); - - const bool i8mm_flag = HasFlag(mcpu, mattr, "+i8mm"); - const bool i8mm_disable = HasFlag(mcpu, mattr, "+noi8mm"); - const bool i8mm_default = arch_version >= 8.6; - const bool i8mm_support = arch_version >= 8.2 && arch_version <= 8.5; - const bool has_i8mm = (i8mm_default && !i8mm_disable) || (i8mm_support && i8mm_flag); + Optional mtriple = Downcast>(target.Get("mtriple")); - const bool dotprod_flag = HasFlag(mcpu, mattr, "+dotprod"); - const bool dotprod_disable = HasFlag(mcpu, mattr, "+nodotprod"); - const bool dotprod_default = arch_version >= 8.4; - const bool dotprod_support = arch_version >= 8.2 && arch_version <= 8.3; - const bool has_dotprod = - (dotprod_default && !dotprod_disable) || (dotprod_support && dotprod_flag); + auto llvm_instance = std::make_unique(); + codegen::LLVMTargetInfo llvm_target(*llvm_instance, target); + Map features = llvm_target.GetAllLLVMCpuFeatures(); - const bool fp16_flag = HasFlag(mcpu, mattr, "+fullfp16"); - const bool fp16_support = arch_version >= 8.2; - const bool has_fp16_simd = fp16_support && (fp16_flag || has_sve); + auto has_feature = [features](const String& feature) { + return features.find(feature) != features.end(); + }; - return {{"is_aarch64", Bool(is_aarch64)}, {"has_asimd", Bool(has_asimd)}, - {"has_sve", Bool(has_sve)}, {"has_dotprod", Bool(has_dotprod)}, - {"has_matmul_i8", Bool(has_i8mm)}, {"has_fp16_simd", Bool(has_fp16_simd)}}; + return {{"is_aarch64", Bool(IsAArch64(mtriple))}, + {"has_asimd", Bool(has_feature("neon"))}, + {"has_sve", Bool(has_feature("sve"))}, + {"has_dotprod", Bool(has_feature("dotprod"))}, + {"has_matmul_i8", Bool(has_feature("i8mm"))}, + {"has_fp16_simd", Bool(has_feature("fullfp16"))}}; } static Array MergeKeys(Optional> existing_keys) { diff --git a/tests/cpp/target/parsers/aprofile_test.cc b/tests/cpp/target/parsers/aprofile_test.cc index fa85d1c32989c..4c08049d21c96 100644 --- a/tests/cpp/target/parsers/aprofile_test.cc +++ b/tests/cpp/target/parsers/aprofile_test.cc @@ -19,6 +19,7 @@ #include "../src/target/parsers/aprofile.h" +#include #include #include @@ -29,6 +30,8 @@ namespace target { namespace parsers { namespace aprofile { +using ::testing::HasSubstr; + static float defaultI8MM = 8.6; static float optionalI8MM[] = {8.2, 8.3, 8.4, 8.5}; static float defaultDotProd = 8.4; @@ -38,15 +41,25 @@ class AProfileOptionalI8MM : public testing::TestWithParam {}; class AProfileOptionalDotProd : public testing::TestWithParam {}; static TargetFeatures ParseTargetWithAttrs(String mcpu, String mtriple, Array mattr) { - return ParseTarget({ - {"mcpu", mcpu}, + TargetJSON target_json = { + {"kind", String("llvm")}, {"mtriple", mtriple}, {"mattr", mattr}, - }); + }; + if (mcpu != "") { + target_json.Set("mcpu", mcpu); + } + return ParseTarget(target_json); +} + +std::string FloatToStringWithoutTrailingZeros(float value) { + std::stringstream ss; + ss << value; + return ss.str(); } TEST(AProfileParser, ParseTargetKeys) { - TargetJSON target = ParseTarget({}); + TargetJSON target = ParseTarget({{"kind", String("llvm")}}); Array keys = Downcast>(target.at("keys")); ASSERT_EQ(keys.size(), 2); ASSERT_EQ(keys[0], "arm_cpu"); @@ -55,6 +68,7 @@ TEST(AProfileParser, ParseTargetKeys) { TEST(AProfileParser, ParseTargetWithExistingKeys) { TargetJSON target = ParseTarget({ + {"kind", String("llvm")}, {"keys", Array{"cpu"}}, }); TargetFeatures features = Downcast(target.at("features")); @@ -66,6 +80,7 @@ TEST(AProfileParser, ParseTargetWithExistingKeys) { TEST(AProfileParser, ParseTargetWithDuplicateKey) { TargetJSON target = ParseTarget({ + {"kind", String("llvm")}, {"keys", Array{"cpu", "arm_cpu"}}, }); TargetFeatures features = Downcast(target.at("features")); @@ -76,13 +91,10 @@ TEST(AProfileParser, ParseTargetWithDuplicateKey) { } TEST(AProfileParser, ParseTargetDefaults) { - TargetJSON target = ParseTarget({}); + TargetJSON target = ParseTarget({{"kind", String("llvm")}}); 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) { @@ -111,6 +123,7 @@ TEST(AProfileParser, IsAArch32Triple) { TEST(AProfileParser, IsAArch32BlankCPU) { TargetJSON target = ParseTarget({ + {"kind", String("llvm")}, {"mtriple", String("arm-unknown-linux-gnu")}, }); TargetFeatures features = Downcast(target.at("features")); @@ -155,11 +168,12 @@ TEST(AProfileParser, AArch64HasASIMD) { ASSERT_EQ(Downcast(features.at("has_asimd")), true); } -TEST(AProfileParser, AArch32NoASIMD) { +TEST(AProfileParser, AArch32ASIMD) { 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); + // TODO(lhutton1) LLVM reports that Neon is support with this target string. Is that true? + ASSERT_EQ(Downcast(features.at("has_asimd")), true); } TEST(AProfileParser, AArch32HasASIMDWithOption) { @@ -167,11 +181,6 @@ TEST(AProfileParser, AArch32HasASIMDWithOption) { 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) { @@ -179,23 +188,10 @@ TEST(AProfileParser, AArch32HasASIMDWithAlternativeOption) { 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"; + std::string arch_attr = "+v" + FloatToStringWithoutTrailingZeros(defaultI8MM) + "a"; TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); TargetFeatures features = Downcast(target.at("features")); ASSERT_EQ(IsArch(target), true); @@ -203,20 +199,15 @@ TEST(AProfileParser, DefaultI8MMSupport) { } TEST(AProfileParser, DefaultI8MMSupportDisable) { - std::string arch_attr = "+v" + std::to_string(defaultI8MM) + "a"; - TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr, "+noi8mm"}); + std::string arch_attr = "+v" + FloatToStringWithoutTrailingZeros(defaultI8MM) + "a"; + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr, "-i8mm"}); 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"; + std::string arch_attr = "+v" + FloatToStringWithoutTrailingZeros(GetParam()) + "a"; TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); TargetFeatures features = Downcast(target.at("features")); @@ -227,23 +218,10 @@ TEST_P(AProfileOptionalI8MM, OptionalI8MMSupport) { 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"; + std::string arch_attr = "+v" + FloatToStringWithoutTrailingZeros(defaultDotProd) + "a"; TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); TargetFeatures features = Downcast(target.at("features")); ASSERT_EQ(IsArch(target), true); @@ -251,20 +229,15 @@ TEST(AProfileParser, DefaultDotProdSupport) { } TEST(AProfileParser, DefaultDotProdSupportDisable) { - std::string arch_attr = "+v" + std::to_string(defaultDotProd) + "a"; - TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr, "+nodotprod"}); + std::string arch_attr = "+v" + FloatToStringWithoutTrailingZeros(defaultDotProd) + "a"; + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr, "-dotprod"}); 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"; + std::string arch_attr = "+v" + FloatToStringWithoutTrailingZeros(GetParam()) + "a"; TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); TargetFeatures features = Downcast(target.at("features")); @@ -275,15 +248,10 @@ TEST_P(AProfileOptionalDotProd, OptionalDotProdSupport) { 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); } TEST(AProfileParser, ArchVersionInvalidLetter) { - std::string arch_attr = "+v" + std::to_string(defaultDotProd) + "b"; + std::string arch_attr = "+v" + FloatToStringWithoutTrailingZeros(defaultDotProd) + "b"; TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); TargetFeatures features = Downcast(target.at("features")); ASSERT_EQ(IsArch(target), true); @@ -292,7 +260,7 @@ TEST(AProfileParser, ArchVersionInvalidLetter) { using AProfileOptionalSVE = testing::TestWithParam; TEST_P(AProfileOptionalSVE, OptionalSVESupport) { - const std::string arch_attr = "+v" + std::to_string(GetParam()) + "a"; + const std::string arch_attr = "+v" + FloatToStringWithoutTrailingZeros(GetParam()) + "a"; // Check that the "has_sve" feature is not set by default when "+sve" isn't set as an attribute. TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); @@ -307,9 +275,25 @@ TEST_P(AProfileOptionalSVE, OptionalSVESupport) { EXPECT_TRUE(Downcast(features.at("has_sve"))); } +TEST(AProfileDefaultSVE, DefaultSVESupportSVESupport) { + const std::string arch_attr = "+v9a"; + + // Check that the "has_sve" feature is not set by default when "+sve" isn't set as an attribute. + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); + TargetFeatures features = Downcast(target.at("features")); + EXPECT_TRUE(IsArch(target)); + EXPECT_TRUE(Downcast(features.at("has_sve"))); + + // Check that the "has_sve" feature is set when "+sve" is explicitly set as an attribute. + target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr, "+sve"}); + features = Downcast(target.at("features")); + EXPECT_TRUE(IsArch(target)); + EXPECT_TRUE(Downcast(features.at("has_sve"))); +} + using AProfileOptionalFP16 = testing::TestWithParam; TEST_P(AProfileOptionalFP16, OptionalFP16Support) { - const std::string arch_attr = "+v" + std::to_string(GetParam()) + "a"; + const std::string arch_attr = "+v" + FloatToStringWithoutTrailingZeros(GetParam()) + "a"; // Check that the "has_fp16_simd" feature is not set by default when "+fullfp16" isn't set as an // attribute. @@ -332,13 +316,57 @@ TEST_P(AProfileOptionalFP16, OptionalFP16Support) { EXPECT_TRUE(Downcast(features.at("has_fp16_simd"))); } +TEST(AProfileDefaultFP16, DefaultFP16Support) { + const std::string arch_attr = "+v9a"; + + // Check that the "has_fp16_simd" feature is not set by default when "+fullfp16" isn't set as an + // attribute. + TargetJSON target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr}); + TargetFeatures features = Downcast(target.at("features")); + EXPECT_TRUE(IsArch(target)); + EXPECT_TRUE(Downcast(features.at("has_fp16_simd"))); + + // Check that the "has_fp16_simd" feature is set when "+fullfp16" is explicitly set as an + // attribute. + target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr, "+fullfp16"}); + features = Downcast(target.at("features")); + EXPECT_TRUE(IsArch(target)); + EXPECT_TRUE(Downcast(features.at("has_fp16_simd"))); + + // Check that the "has_fp16_simd" feature is set when "+sve" is explicitly set as an attribute. + target = ParseTargetWithAttrs("", "aarch64-arm-none-eabi", {arch_attr, "+sve"}); + features = Downcast(target.at("features")); + EXPECT_TRUE(IsArch(target)); + EXPECT_TRUE(Downcast(features.at("has_fp16_simd"))); +} + +TEST(AProfileParser, ImpliedFeature) { + TargetJSON target = ParseTargetWithAttrs("", "aarch64-linux-gnu", {"+sve2"}); + TargetFeatures features = Downcast(target.at("features")); + EXPECT_TRUE(Downcast(features.at("has_sve"))); + EXPECT_TRUE(Downcast(features.at("has_asimd"))); +} + +TEST(AProfileParser, UnexpectedTargetKind) { + EXPECT_THROW( + { + try { + ParseTarget({{"kind", String("c")}}); + } catch (const tvm::InternalError& e) { + EXPECT_THAT(e.what(), HasSubstr("Expected target kind 'llvm', but got 'c'")); + throw; + } + }, + tvm::InternalError); +} + INSTANTIATE_TEST_CASE_P(AProfileParser, AProfileOptionalI8MM, ::testing::ValuesIn(optionalI8MM)); INSTANTIATE_TEST_CASE_P(AProfileParser, AProfileOptionalDotProd, ::testing::ValuesIn(optionalDotProd)); INSTANTIATE_TEST_CASE_P(AProfileParser, AProfileOptionalSVE, - ::testing::Values(8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9.0)); + ::testing::Values(8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9)); INSTANTIATE_TEST_CASE_P(AProfileParser, AProfileOptionalFP16, - ::testing::Values(8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9.0)); + ::testing::Values(8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9)); } // namespace aprofile } // namespace parsers diff --git a/tests/python/relay/strategy/test_select_implementation.py b/tests/python/relay/strategy/test_select_implementation.py index f9b1a002a8b66..139f8e924a0d0 100644 --- a/tests/python/relay/strategy/test_select_implementation.py +++ b/tests/python/relay/strategy/test_select_implementation.py @@ -120,7 +120,7 @@ def _get_conv2d_impl(dtype, target): ), ( "llvm --device=arm_cpu --mtriple=aarch64-linux-gnu -mattr=+v9a", - "conv2d_NHWC_quantized_interleaved_without_transform.arm_cpu", + "conv2d_NHWC_quantized_native_without_transform.arm_cpu", ), ], )