https://github.com/Ami-zhang updated https://github.com/llvm/llvm-project/pull/140700
>From 3355685f4debcd901f27efccc346de0f302a796c Mon Sep 17 00:00:00 2001 From: Ami-zhang <zhangli...@loongson.cn> Date: Wed, 14 May 2025 15:21:54 +0800 Subject: [PATCH 1/2] [Clang][LoongArch] Support target attribute for function This adds support under LoongArch for the target("..") attributes. The supported formats are: - "arch=<arch>" strings, that specify the architecture features for a function as per the -march=arch option. - "tune=<cpu>" strings, that specify the tune-cpu cpu for a function as per -mtune. - "<feature>", "no-<feature>" enabled/disables the specific feature. --- clang/lib/Basic/Targets/LoongArch.cpp | 49 ++++++++++ clang/lib/Basic/Targets/LoongArch.h | 4 + clang/lib/Sema/SemaDeclAttr.cpp | 11 +++ clang/test/CodeGen/LoongArch/targetattr.c | 92 +++++++++++++++++++ clang/test/Sema/attr-target-loongarch.c | 19 ++++ .../llvm/TargetParser/LoongArchTargetParser.h | 1 + .../TargetParser/LoongArchTargetParser.cpp | 11 +++ 7 files changed, 187 insertions(+) create mode 100644 clang/test/CodeGen/LoongArch/targetattr.c create mode 100644 clang/test/Sema/attr-target-loongarch.c diff --git a/clang/lib/Basic/Targets/LoongArch.cpp b/clang/lib/Basic/Targets/LoongArch.cpp index ca742797d7a3b..aee273a76a608 100644 --- a/clang/lib/Basic/Targets/LoongArch.cpp +++ b/clang/lib/Basic/Targets/LoongArch.cpp @@ -388,6 +388,51 @@ bool LoongArchTargetInfo::handleTargetFeatures( return true; } +ParsedTargetAttr +LoongArchTargetInfo::parseTargetAttr(StringRef Features) const { + ParsedTargetAttr Ret; + if (Features == "default") + return Ret; + SmallVector<StringRef, 1> AttrFeatures; + Features.split(AttrFeatures, ","); + + for (auto &Feature : AttrFeatures) { + Feature = Feature.trim(); + + if (Feature.starts_with("arch=")) { + StringRef ArchValue = Feature.split("=").second.trim(); + + if (llvm::LoongArch::isValidArchName(ArchValue) || + ArchValue == "la64v1.0" || ArchValue == "la64v1.1") { + std::vector<llvm::StringRef> ArchFeatures; + if (llvm::LoongArch::getArchFeatures(ArchValue, ArchFeatures)) { + Ret.Features.insert(Ret.Features.end(), ArchFeatures.begin(), + ArchFeatures.end()); + } + + if (!Ret.CPU.empty()) + Ret.Duplicate = "arch="; + else if (ArchValue == "la64v1.0" || ArchValue == "la64v1.1") + Ret.CPU = "loongarch64"; + else + Ret.CPU = ArchValue; + } else { + Ret.Features.push_back("!arch=" + ArchValue.str()); + } + } else if (Feature.starts_with("tune=")) { + if (!Ret.Tune.empty()) + Ret.Duplicate = "tune="; + else + Ret.Tune = Feature.split("=").second.trim(); + } else if (Feature.starts_with("no-")) { + Ret.Features.push_back("-" + Feature.split("-").second.str()); + } else { + Ret.Features.push_back("+" + Feature.str()); + } + } + return Ret; +} + bool LoongArchTargetInfo::isValidCPUName(StringRef Name) const { return llvm::LoongArch::isValidCPUName(Name); } @@ -396,3 +441,7 @@ void LoongArchTargetInfo::fillValidCPUList( SmallVectorImpl<StringRef> &Values) const { llvm::LoongArch::fillValidCPUList(Values); } + +bool LoongArchTargetInfo::isValidFeatureName(StringRef Name) const { + return llvm::LoongArch::isValidFeatureName(Name); +} diff --git a/clang/lib/Basic/Targets/LoongArch.h b/clang/lib/Basic/Targets/LoongArch.h index 4c7b53abfef9b..a83bb925bc310 100644 --- a/clang/lib/Basic/Targets/LoongArch.h +++ b/clang/lib/Basic/Targets/LoongArch.h @@ -101,6 +101,9 @@ class LLVM_LIBRARY_VISIBILITY LoongArchTargetInfo : public TargetInfo { bool handleTargetFeatures(std::vector<std::string> &Features, DiagnosticsEngine &Diags) override; + ParsedTargetAttr parseTargetAttr(StringRef Str) const override; + bool supportsTargetAttributeTune() const override { return true; } + bool initFeatureMap(llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, StringRef CPU, @@ -110,6 +113,7 @@ class LLVM_LIBRARY_VISIBILITY LoongArchTargetInfo : public TargetInfo { bool isValidCPUName(StringRef Name) const override; void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override; + bool isValidFeatureName(StringRef Name) const override; }; class LLVM_LIBRARY_VISIBILITY LoongArch32TargetInfo diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 4d7f0455444f1..8e44e2e44bb24 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3195,6 +3195,17 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) { } } + if (Context.getTargetInfo().getTriple().isLoongArch()) { + for (const auto &Feature : ParsedAttrs.Features) { + StringRef CurFeature = Feature; + if (CurFeature.starts_with("!arch=")) { + StringRef ArchValue = CurFeature.split("=").second.trim(); + return Diag(LiteralLoc, diag::err_attribute_unsupported) + << "target(arch=..)" << ArchValue; + } + } + } + if (ParsedAttrs.Duplicate != "") return Diag(LiteralLoc, diag::warn_unsupported_target_attribute) << Duplicate << None << ParsedAttrs.Duplicate << Target; diff --git a/clang/test/CodeGen/LoongArch/targetattr.c b/clang/test/CodeGen/LoongArch/targetattr.c new file mode 100644 index 0000000000000..a99e12051452b --- /dev/null +++ b/clang/test/CodeGen/LoongArch/targetattr.c @@ -0,0 +1,92 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals all --version 5 +// RUN: %clang --target=loongarch64-linux-gnu %s -S -emit-llvm -o - \ +// RUN: | FileCheck %s + +__attribute__((target("div32"))) +// CHECK-LABEL: define dso_local void @testdiv32( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void testdiv32() {} + +__attribute__((target("arch=loongarch64"))) +// CHECK-LABEL: define dso_local void @testLoongarch64( +// CHECK-SAME: ) #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void testLoongarch64() {} + +__attribute__((target("arch=la64v1.0"))) +// CHECK-LABEL: define dso_local void @testLa64v10( +// CHECK-SAME: ) #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void testLa64v10() {} + +__attribute__((target("arch=la64v1.1"))) +// CHECK-LABEL: define dso_local void @testLa64v11( +// CHECK-SAME: ) #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void testLa64v11() {} + +__attribute__((target("arch=la464"))) +// CHECK-LABEL: define dso_local void @testLa464( +// CHECK-SAME: ) #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void testLa464() {} + +__attribute__((target("arch=la664"))) +// CHECK-LABEL: define dso_local void @testLa664( +// CHECK-SAME: ) #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void testLa664() {} + +__attribute__((target("arch=la664, no-div32"))) +// CHECK-LABEL: define dso_local void @la664Nodiv32( +// CHECK-SAME: ) #[[ATTR5:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void la664Nodiv32() {} + +__attribute__((target("tune=la464"))) +// CHECK-LABEL: define dso_local void @tuneLa664( +// CHECK-SAME: ) #[[ATTR6:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void tuneLa664() {} + +__attribute__((target("arch=la464, tune=la664"))) +// CHECK-LABEL: define dso_local void @archLa464tuneLa664( +// CHECK-SAME: ) #[[ATTR7:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void archLa464tuneLa664() {} + +//. +// CHECK: attributes #[[ATTR0]] = { noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="loongarch64" "target-features"="+64bit,+d,+div32,+f,+lsx,+ual" } +// CHECK: attributes #[[ATTR1]] = { noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="loongarch64" "target-features"="+64bit,+d,+f,+lsx,+ual" } +// CHECK: attributes #[[ATTR2]] = { noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="loongarch64" "target-features"="+64bit,+d,+div32,+f,+frecipe,+lam-bh,+lamcas,+ld-seq-sa,+lsx,+scq,+ual" } +// CHECK: attributes #[[ATTR3]] = { noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="la464" "target-features"="+64bit,+d,+f,+lasx,+lsx,+ual" } +// CHECK: attributes #[[ATTR4]] = { noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="la664" "target-features"="+64bit,+d,+div32,+f,+frecipe,+lam-bh,+lamcas,+lasx,+ld-seq-sa,+lsx,+scq,+ual" } +// CHECK: attributes #[[ATTR5]] = { noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="la664" "target-features"="+64bit,+d,+f,+frecipe,+lam-bh,+lamcas,+lasx,+ld-seq-sa,+lsx,+scq,+ual,-div32" } +// CHECK: attributes #[[ATTR6]] = { noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="loongarch64" "target-features"="+64bit,+d,+f,+lsx,+ual" "tune-cpu"="la464" } +// CHECK: attributes #[[ATTR7]] = { noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="la464" "target-features"="+64bit,+d,+f,+lasx,+lsx,+ual" "tune-cpu"="la664" } +//. +// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4} +// CHECK: [[META1:![0-9]+]] = !{i32 8, !"PIC Level", i32 2} +// CHECK: [[META2:![0-9]+]] = !{i32 7, !"PIE Level", i32 2} +// CHECK: [[META3:![0-9]+]] = !{i32 7, !"frame-pointer", i32 2} +// CHECK: [[META4:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"} +//. diff --git a/clang/test/Sema/attr-target-loongarch.c b/clang/test/Sema/attr-target-loongarch.c new file mode 100644 index 0000000000000..0dfe23fb1cb3f --- /dev/null +++ b/clang/test/Sema/attr-target-loongarch.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -triple loongarch64-linux-gnu -fsyntax-only -verify %s + +// expected-error@+1 {{function multiversioning is not supported on the current target}} +void __attribute__((target("default"))) bar(void) {} + +// expected-error@+1 {{target(arch=..) attribute is not supported on targets missing invalid; specify an appropriate -march= or -mcpu=}} +void __attribute__((target("arch=invalid"))) foo(void) {} + +// expected-warning@+1 {{unsupported 'aaa' in the 'target' attribute string; 'target' attribute ignored}} +int __attribute__((target("aaa"))) test_feature(void) { return 4; } + +// expected-warning@+1 {{unsupported 'aaa' in the 'target' attribute string; 'target' attribute ignored}} +int __attribute__((target("no-aaa"))) test_nofeature(void) { return 4; } + +// expected-warning@+1 {{duplicate 'arch=' in the 'target' attribute string; 'target' attribute ignored}} +int __attribute__((target("arch=la464,arch=la664"))) test_duplarch(void) { return 4; } + +// expected-warning@+1 {{unknown tune CPU 'la64v1.0' in the 'target' attribute string; 'target' attribute ignored}} +int __attribute__((target("tune=la64v1.0"))) test_tune(void) { return 4; } diff --git a/llvm/include/llvm/TargetParser/LoongArchTargetParser.h b/llvm/include/llvm/TargetParser/LoongArchTargetParser.h index e08e7bc182e11..c425ce271c559 100644 --- a/llvm/include/llvm/TargetParser/LoongArchTargetParser.h +++ b/llvm/include/llvm/TargetParser/LoongArchTargetParser.h @@ -85,6 +85,7 @@ struct ArchInfo { }; bool isValidArchName(StringRef Arch); +bool isValidFeatureName(StringRef Arch); bool getArchFeatures(StringRef Arch, std::vector<StringRef> &Features); bool isValidCPUName(StringRef TuneCPU); void fillValidCPUList(SmallVectorImpl<StringRef> &Values); diff --git a/llvm/lib/TargetParser/LoongArchTargetParser.cpp b/llvm/lib/TargetParser/LoongArchTargetParser.cpp index e394c0c15b207..7bd1711538d25 100644 --- a/llvm/lib/TargetParser/LoongArchTargetParser.cpp +++ b/llvm/lib/TargetParser/LoongArchTargetParser.cpp @@ -34,6 +34,17 @@ bool LoongArch::isValidArchName(StringRef Arch) { return false; } +bool LoongArch::isValidFeatureName(StringRef Feature) { + Feature = Feature.starts_with("+") ? Feature.drop_front() : Feature; + for (const auto F : AllFeatures) { + StringRef CanonicalName = + F.Name.starts_with("+") ? F.Name.drop_front() : F.Name; + if (CanonicalName == Feature) + return true; + } + return false; +} + bool LoongArch::getArchFeatures(StringRef Arch, std::vector<StringRef> &Features) { for (const auto A : AllArchs) { >From d6c9c7e2cb7a0edb77254329315590094331543f Mon Sep 17 00:00:00 2001 From: Ami-zhang <zhangli...@loongson.cn> Date: Fri, 23 May 2025 17:36:47 +0800 Subject: [PATCH 2/2] [Loongarch] Refactor target attribute parsing Introduce AttrFeatureKind enum to classify target attributes, and implement getAttrFeatureTypeAndValue() helper for centralized parsing. --- clang/lib/Basic/Targets/LoongArch.cpp | 57 +++++++++++++++++++-------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/clang/lib/Basic/Targets/LoongArch.cpp b/clang/lib/Basic/Targets/LoongArch.cpp index aee273a76a608..e6e869763620b 100644 --- a/clang/lib/Basic/Targets/LoongArch.cpp +++ b/clang/lib/Basic/Targets/LoongArch.cpp @@ -388,6 +388,21 @@ bool LoongArchTargetInfo::handleTargetFeatures( return true; } +enum class AttrFeatureKind { Arch, Tune, NoFeature, Feature, Invalid }; + +static std::pair<AttrFeatureKind, llvm::StringRef> +getAttrFeatureTypeAndValue(llvm::StringRef AttrFeature) { + if (auto Split = AttrFeature.split("="); !Split.second.empty()) { + if (Split.first.trim() == "arch") + return {AttrFeatureKind::Arch, Split.second.trim()}; + if (Split.first.trim() == "tune") + return {AttrFeatureKind::Tune, Split.second.trim()}; + } + if (AttrFeature.starts_with("no-")) + return {AttrFeatureKind::NoFeature, AttrFeature.drop_front(3).trim()}; + return {AttrFeatureKind::Feature, AttrFeature.trim()}; +} + ParsedTargetAttr LoongArchTargetInfo::parseTargetAttr(StringRef Features) const { ParsedTargetAttr Ret; @@ -397,37 +412,47 @@ LoongArchTargetInfo::parseTargetAttr(StringRef Features) const { Features.split(AttrFeatures, ","); for (auto &Feature : AttrFeatures) { - Feature = Feature.trim(); + auto [Kind, Value] = getAttrFeatureTypeAndValue(Feature.trim()); - if (Feature.starts_with("arch=")) { - StringRef ArchValue = Feature.split("=").second.trim(); - - if (llvm::LoongArch::isValidArchName(ArchValue) || - ArchValue == "la64v1.0" || ArchValue == "la64v1.1") { + switch (Kind) { + case AttrFeatureKind::Arch: { + if (llvm::LoongArch::isValidArchName(Value) || Value == "la64v1.0" || + Value == "la64v1.1") { std::vector<llvm::StringRef> ArchFeatures; - if (llvm::LoongArch::getArchFeatures(ArchValue, ArchFeatures)) { + if (llvm::LoongArch::getArchFeatures(Value, ArchFeatures)) { Ret.Features.insert(Ret.Features.end(), ArchFeatures.begin(), ArchFeatures.end()); } if (!Ret.CPU.empty()) Ret.Duplicate = "arch="; - else if (ArchValue == "la64v1.0" || ArchValue == "la64v1.1") + else if (Value == "la64v1.0" || Value == "la64v1.1") Ret.CPU = "loongarch64"; else - Ret.CPU = ArchValue; + Ret.CPU = Value; } else { - Ret.Features.push_back("!arch=" + ArchValue.str()); + Ret.Features.push_back("!arch=" + Value.str()); } - } else if (Feature.starts_with("tune=")) { + break; + } + + case AttrFeatureKind::Tune: if (!Ret.Tune.empty()) Ret.Duplicate = "tune="; else - Ret.Tune = Feature.split("=").second.trim(); - } else if (Feature.starts_with("no-")) { - Ret.Features.push_back("-" + Feature.split("-").second.str()); - } else { - Ret.Features.push_back("+" + Feature.str()); + Ret.Tune = Value; + break; + + case AttrFeatureKind::NoFeature: + Ret.Features.push_back("-" + Value.str()); + break; + + case AttrFeatureKind::Feature: + Ret.Features.push_back("+" + Value.str()); + break; + + case AttrFeatureKind::Invalid: + break; } } return Ret; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits