Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions clang/lib/Basic/Targets/LoongArch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,73 @@ bool LoongArchTargetInfo::handleTargetFeatures(
return true;
}

enum class AttrFeatureKind { Arch, Tune, NoFeature, Feature };

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)};
return {AttrFeatureKind::Feature, AttrFeature};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default return may cause us to miss the check for invalid strings. (like this: "+lasx", here will be two plus signs in the generated IR) . Perhaps we can perform the check inside isValidFeature()?"

Copy link
Contributor Author

@Ami-zhang Ami-zhang May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appreciate the review suggestion. The validation will be handled in isValidFeatureName(), with corresponding test cases added.Thanks!

}

ParsedTargetAttr
LoongArchTargetInfo::parseTargetAttr(StringRef Features) const {
ParsedTargetAttr Ret;
if (Features == "default")
return Ret;
SmallVector<StringRef, 1> AttrFeatures;
Features.split(AttrFeatures, ",");

for (auto &Feature : AttrFeatures) {
auto [Kind, Value] = getAttrFeatureTypeAndValue(Feature.trim());

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(Value, ArchFeatures)) {
Ret.Features.insert(Ret.Features.end(), ArchFeatures.begin(),
ArchFeatures.end());
}

if (!Ret.CPU.empty())
Ret.Duplicate = "arch=";
else if (Value == "la64v1.0" || Value == "la64v1.1")
Ret.CPU = "loongarch64";
else
Ret.CPU = Value;
} else {
Ret.Features.push_back("!arch=" + Value.str());
}
break;
}

case AttrFeatureKind::Tune:
if (!Ret.Tune.empty())
Ret.Duplicate = "tune=";
else
Ret.Tune = Value;
break;

case AttrFeatureKind::NoFeature:
Ret.Features.push_back("-" + Value.str());
break;

case AttrFeatureKind::Feature:
Ret.Features.push_back("+" + Value.str());
break;
}
}
return Ret;
}

bool LoongArchTargetInfo::isValidCPUName(StringRef Name) const {
return llvm::LoongArch::isValidCPUName(Name);
}
Expand All @@ -396,3 +463,7 @@ void LoongArchTargetInfo::fillValidCPUList(
SmallVectorImpl<StringRef> &Values) const {
llvm::LoongArch::fillValidCPUList(Values);
}

bool LoongArchTargetInfo::isValidFeatureName(StringRef Name) const {
return llvm::LoongArch::isValidFeatureName(Name);
}
4 changes: 4 additions & 0 deletions clang/lib/Basic/Targets/LoongArch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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=")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to LoongArchTargetInfo::parseTargetAttr(), this condition may always be false.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake — I didn't notice the !.

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;
Expand Down
92 changes: 92 additions & 0 deletions clang/test/CodeGen/LoongArch/targetattr.c
Original file line number Diff line number Diff line change
@@ -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 @tuneLa464(
// CHECK-SAME: ) #[[ATTR6:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: ret void
//
void tuneLa464() {}

__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 {{.*}}"}
//.
25 changes: 25 additions & 0 deletions clang/test/Sema/attr-target-loongarch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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 '+div32' in the 'target' attribute string; 'target' attribute ignored}}
void __attribute__((target("+div32"))) plusfeature(void) {}

// expected-warning@+1 {{unsupported '-div32' in the 'target' attribute string; 'target' attribute ignored}}
void __attribute__((target("-div32"))) minusfeature(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; }
1 change: 1 addition & 0 deletions llvm/include/llvm/TargetParser/LoongArchTargetParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ struct ArchInfo {
};

bool isValidArchName(StringRef Arch);
bool isValidFeatureName(StringRef Feature);
bool getArchFeatures(StringRef Arch, std::vector<StringRef> &Features);
bool isValidCPUName(StringRef TuneCPU);
void fillValidCPUList(SmallVectorImpl<StringRef> &Values);
Expand Down
12 changes: 12 additions & 0 deletions llvm/lib/TargetParser/LoongArchTargetParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ bool LoongArch::isValidArchName(StringRef Arch) {
return false;
}

bool LoongArch::isValidFeatureName(StringRef Feature) {
if (Feature.starts_with("+") || Feature.starts_with("-"))
return false;
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) {
Expand Down