diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h --- a/clang/lib/Basic/Targets/RISCV.h +++ b/clang/lib/Basic/Targets/RISCV.h @@ -109,6 +109,9 @@ void fillValidCPUList(SmallVectorImpl &Values) const override; bool isValidTuneCPUName(StringRef Name) const override; void fillValidTuneCPUList(SmallVectorImpl &Values) const override; + bool supportsTargetAttributeTune() const override { return true; } + bool validateCpuSupports(StringRef FeatureStr) const override; + ParsedTargetAttr parseTargetAttr(StringRef Str) const override; }; class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo { public: diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp --- a/clang/lib/Basic/Targets/RISCV.cpp +++ b/clang/lib/Basic/Targets/RISCV.cpp @@ -250,12 +250,17 @@ // RISCVISAInfo makes implications for ISA features std::vector ImpliedFeatures = (*ParseResult)->toFeatureVector(); + std::vector UpdatedFeatures; + // Add non-ISA features like `relax` and `save-restore` back for (const std::string &Feature : FeaturesVec) if (!llvm::is_contained(ImpliedFeatures, Feature)) - ImpliedFeatures.push_back(Feature); + UpdatedFeatures.push_back(Feature); + + UpdatedFeatures.insert(UpdatedFeatures.end(), ImpliedFeatures.begin(), + ImpliedFeatures.end()); - return TargetInfo::initFeatureMap(Features, Diags, CPU, ImpliedFeatures); + return TargetInfo::initFeatureMap(Features, Diags, CPU, UpdatedFeatures); } std::optional> @@ -346,3 +351,77 @@ bool Is64Bit = getTriple().isArch64Bit(); llvm::RISCV::fillValidTuneCPUArchList(Values, Is64Bit); } + +// Parse RISC-V Target attributes, which are a comma separated list of: +// "arch=" - parsed to features as per -march=.. +// "cpu=" - parsed to features as per -mcpu=.., with CPU set to +// "tune=" - TuneCPU set to +ParsedTargetAttr RISCVTargetInfo::parseTargetAttr(StringRef Features) const { + ParsedTargetAttr Ret; + if (Features == "default") + return Ret; + SmallVector AttrFeatures; + Features.split(AttrFeatures, ";"); + bool FoundArch = false; + + for (auto &Feature : AttrFeatures) { + Feature = Feature.trim(); + StringRef AttrString = Feature.split("=").second.trim(); + + if (Feature.startswith("arch=")) { + if (FoundArch) + Ret.Duplicate = "arch="; + FoundArch = true; + + if (AttrString.startswith("+") || AttrString.startswith("-")) { + // EXTENSION like arch=+v,+zbb,-c + SmallVector Exts; + AttrString.split(Exts, ","); + for (auto Ext : Exts) { + if (Ext.empty()) + continue; + + StringRef ExtName = Ext.substr(1); + std::string TargetFeature = + llvm::RISCVISAInfo::getTargetFeatureForExtension(ExtName); + if (!TargetFeature.empty()) + Ret.Features.push_back(Ext.front() + TargetFeature); + else + Ret.Features.push_back(Ext.str()); + } + } else { + // full-arch-string like arch=rv64gcv + auto RII = llvm::RISCVISAInfo::parseArchString( + AttrString, /* EnableExperimentalExtension */ true); + if (!RII) { + consumeError(RII.takeError()); + } else { + std::vector FeatStrings = (*RII)->toFeatureVector(); + for (auto FeatString : FeatStrings) + Ret.Features.push_back(FeatString); + } + } + continue; + } else if (Feature.startswith("cpu=")) { + if (!Ret.CPU.empty()) + Ret.Duplicate = "cpu="; + else + Ret.CPU = AttrString; + continue; + } else if (Feature.startswith("tune=")) { + if (!Ret.Tune.empty()) + Ret.Duplicate = "tune="; + else + Ret.Tune = AttrString; + continue; + } else if (Feature.startswith("no-")) + Ret.Features.push_back("-" + Feature.split("-").second.str()); + else + Ret.Features.push_back("+" + Feature.str()); + } + return Ret; +} + +bool RISCVTargetInfo::validateCpuSupports(StringRef FeatureStr) const { + return ISAInfo->isSupportedExtensionFeature(FeatureStr); +} diff --git a/clang/test/CodeGen/RISCV/riscv-func-attr-target.c b/clang/test/CodeGen/RISCV/riscv-func-attr-target.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/RISCV/riscv-func-attr-target.c @@ -0,0 +1,152 @@ +// REQUIRES: riscv-registered-target +// RUN: %clang_cc1 -triple riscv64 -target-feature +zifencei \ +// RUN: -target-feature +m -target-feature +a \ +// RUN: -target-feature +f -target-feature +d %s \ +// RUN: -S -o - | FileCheck %s --check-prefix=CHECK-ASM +// RUN: %clang_cc1 -triple riscv64 -target-feature +zifencei \ +// RUN: -target-feature +m -target-feature +a \ +// RUN: -target-feature +f -target-feature +d \ +// RUN: -emit-llvm %s -o - | FileCheck %s \ +// RUN: --check-prefix=CHECK-IR + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, -a, +v, +zbb, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +// CHECK-ASM-LABEL: test1 +// CHECK-ASM: .option pop +// CHECK-IR: void @test1() #0 +__attribute__((target("arch=-a,+v,+zbb"))) void test1() {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +c +// CHECK-ASM-LABEL: test2 +// CHECK-ASM: .option pop +// CHECK-IR: void @test2() #1 +__attribute__((target("arch=rv64gc"))) void test2 () {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +c +// CHECK-ASM-LABEL: test3 +// CHECK-ASM: .option pop +// CHECK-IR: void @test3() #1 +__attribute__((target("arch=+c"))) void test3 () {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +v, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +// CHECK-ASM-LABEL: test4 +// CHECK-ASM: .option pop +// CHECK-IR: void @test4() #2 +__attribute__((target("arch=+v"))) void test4 () {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, -a, +v, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +// CHECK-ASM-LABEL: test5 +// CHECK-ASM: .option pop +// CHECK-IR: void @test5() #3 +__attribute__((target("arch=-a,+v"))) void test5() {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +zbb +// CHECK-ASM-LABEL: test6 +// CHECK-ASM: .option pop +// CHECK-IR: void @test6() #4 +__attribute__((target("arch=rv64g_zbb"))) void test6() {} + +// CHECK-ASM-LABEL: test7 +// CHECK-ASM-NOT: .option pop +// CHECK-IR: void @test7() #5 +__attribute__((target("cpu=rocket-rv64"))) void test7 () {} + +// CHECK-ASM-LABEL: test8 +// CHECK-ASM-NOT: .option pop +// CHECK-IR: void @test8() #6 +__attribute__((target("tune=rocket-rv64"))) void test8 () {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +c +// CHECK-ASM-LABEL: test9 +// CHECK-ASM: .option pop +// CHECK-IR: void @test9() #7 +__attribute__((target("cpu=rocket-rv64;arch=+c"))) void test9 () {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +c, +v, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +// CHECK-ASM-LABEL: test10 +// CHECK-ASM: .option pop +// CHECK-IR: void @test10() #8 +__attribute__((target("cpu=rocket-rv64;arch=rv64gcv"))) void test10 () {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +c, +v, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +// CHECK-ASM-LABEL: test11 +// CHECK-ASM: .option pop +// CHECK-IR: void @test11() #9 +__attribute__((target("tune=rocket-rv64;arch=rv64gcv"))) void test11 () {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +v, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +// CHECK-ASM-LABEL: test12 +// CHECK-ASM: .option pop +// CHECK-IR: void @test12() #10 +__attribute__((target("cpu=rocket-rv64;tune=generic-rv64;arch=+v"))) void test12 () {} + +// CHECK-ASM-LABEL: test13 +// CHECK-ASM-NOT: .option pop +// CHECK-IR: void @test13() #11 +__attribute__((target("cpu=rocket-rv64;tune=generic-rv64"))) void test13 () {} + +// CHECK-ASM-LABEL: test14 +// CHECK-ASM-NOT: .option pop +// CHECK-IR: void @test14() #11 +__attribute__((target("tune=generic-rv64;cpu=rocket-rv64"))) void test14 () {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +v, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +// CHECK-ASM-LABEL: test15 +// CHECK-ASM: .option pop +// CHECK-IR: void @test15() #10 +__attribute__((target("arch=+v;tune=generic-rv64;cpu=rocket-rv64"))) void test15 () {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +v, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +// CHECK-ASM-LABEL: test16 +// CHECK-ASM: .option pop +// CHECK-IR: void @test16() #10 +__attribute__((target("tune=generic-rv64;arch=+v;cpu=rocket-rv64"))) void test16 () {} + + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +c +// CHECK-ASM-LABEL: test17 +// CHECK-ASM: .option pop +// CHECK-IR: void @test17() #12 +__attribute__((target("tune=rocket-rv64;arch=rv64gc"))) void test17 () {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +c +// CHECK-ASM-LABEL: test18 +// CHECK-ASM: .option pop +// CHECK-IR: void @test18() #13 +__attribute__((target("arch=rv64gczihintntl0p2"))) void test18 () {} + +// CHECK-ASM: .option push +// CHECK-ASM-NEXT: .option arch, +c, +experimental-zihintntl +// CHECK-ASM-LABEL: test19 +// CHECK-ASM: .option pop +// CHECK-IR: void @test19() #13 +__attribute__((target("arch=+zihintntl0p2,+c"))) void test19 () {} + + +// CHECK-IR: attributes #0 {{.*}}+v,+zbb{{.*}} +// CHECK-IR: attributes #1 {{.*}}+c{{.*}} +// CHECK-IR: attributes #2 {{.*}}+v{{.*}} +// CHECK-IR: attributes #3 {{.*}}-a{{.*}} +// CHECK-IR: attributes #4 {{.*}}+zbb{{.*}} +// CHECK-IR: attributes #5 {{.*}} "target-cpu"="rocket-rv64" {{.*}} +// CHECK-IR: attributes #6 {{.*}} "tune-cpu"="rocket-rv64" {{.*}} +// CHECK-IR: attributes #7 {{.*}} "target-cpu"="rocket-rv64" {{.*}} +// CHECK-IR: attributes #8 {{.*}} "target-cpu"="rocket-rv64" {{.*}} +// CHECK-IR: attributes #9 {{.*}} "tune-cpu"="rocket-rv64" {{.*}} +// CHECK-IR: attributes #10 {{.*}} "target-cpu"="rocket-rv64" {{.*}} "tune-cpu"="generic-rv64" {{.*}} +// CHECK-IR: attributes #11 {{.*}} "target-cpu"="rocket-rv64" {{.*}} "tune-cpu"="generic-rv64" {{.*}} +// CHECK-IR: attributes #12 {{.*}} "tune-cpu"="rocket-rv64" {{.*}} +// CHECK-IR: attributes #13 {{.*}}+experimental-zihintntl{{.*}} diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp --- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp +++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -36,6 +36,7 @@ #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/RISCVISAInfo.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" @@ -46,6 +47,10 @@ STATISTIC(RISCVNumInstrsCompressed, "Number of RISC-V Compressed instructions emitted"); +namespace llvm { +extern const SubtargetFeatureKV RISCVFeatureKV[RISCV::NumSubtargetFeatures]; +} // namespace llvm + namespace { class RISCVAsmPrinter : public AsmPrinter { const RISCVSubtarget *STI; @@ -83,6 +88,8 @@ void emitEndOfAsmFile(Module &M) override; void emitFunctionEntryLabel() override; + void emitDirectiveOptionArch(); + bool isSameAttribute(); private: void emitAttributes(); @@ -238,11 +245,43 @@ return false; } +void RISCVAsmPrinter::emitDirectiveOptionArch() { + RISCVTargetStreamer &RTS = + static_cast(*OutStreamer->getTargetStreamer()); + SmallVector NeedEmitStdOptionArgs; + const MCSubtargetInfo &MCSTI = *TM.getMCSubtargetInfo(); + for (const auto &Feature : RISCVFeatureKV) { + if (!llvm::RISCVISAInfo::isSupportedExtensionFeature(Feature.Key)) + continue; + if (STI->hasFeature(Feature.Value) && !MCSTI.hasFeature(Feature.Value)) + NeedEmitStdOptionArgs.emplace_back(RISCVOptionArchArgType::Plus, + Feature.Key); + if (!STI->hasFeature(Feature.Value) && MCSTI.hasFeature(Feature.Value)) + NeedEmitStdOptionArgs.emplace_back(RISCVOptionArchArgType::Minus, + Feature.Key); + } + RTS.emitDirectiveOptionArch(NeedEmitStdOptionArgs); +} + +bool RISCVAsmPrinter::isSameAttribute() { + const MCSubtargetInfo &MCSTI = *TM.getMCSubtargetInfo(); + return MCSTI.getFeatureBits() == STI->getFeatureBits(); +} + bool RISCVAsmPrinter::runOnMachineFunction(MachineFunction &MF) { STI = &MF.getSubtarget(); + RISCVTargetStreamer &RTS = + static_cast(*OutStreamer->getTargetStreamer()); + if (!isSameAttribute()) { + RTS.emitDirectiveOptionPush(); + emitDirectiveOptionArch(); + } SetupMachineFunction(MF); emitFunctionBody(); + + if (!isSameAttribute()) + RTS.emitDirectiveOptionPop(); return false; } diff --git a/llvm/test/CodeGen/RISCV/riscv-func-attr-target.ll b/llvm/test/CodeGen/RISCV/riscv-func-attr-target.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/riscv-func-attr-target.ll @@ -0,0 +1,46 @@ +; RUN: llc -mtriple=riscv64 -mattr=+a,+d,+f,+m -verify-machineinstrs < %s | FileCheck %s + +; CHECK: .option push +; CHECK-NEXT: .option arch, +c, +v, +zifencei, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +define void @test1() "target-features"="+a,+d,+f,+m,+c,+v,+zifencei,+zve32f,+zve32x,+zve64d,+zve64f,+zve64x,+zvl128b,+zvl32b,+zvl64b" { +; CHECK-LABEL: test1 +; CHECL: .option pop +entry: + ret void +} + +; CHECK: .option push +; CHECK-NEXT: .option arch, +c, +zifencei +define void @test2() "target-features"="+a,+d,+f,+m,+c,+zifencei" { +; CHECK-LABEL: test2 +; CHECL: .option pop +entry: + ret void +} + +; CHECK: .option push +; CHECK-NEXT: .option arch, +v, +zifencei, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +define void @test3() "target-features"="+a,+d,+f,+m,+v,+zifencei,+zve32f,+zve32x,+zve64d,+zve64f,+zve64x,+zvl128b,+zvl32b,+zvl64b" { +; CHECK-LABEL: test3 +; CHECL: .option pop +entry: + ret void +} + +; CHECK: .option push +; CHECK-NEXT: .option arch, +experimental-zihintntl, +zifencei +define void @test4() "target-features"="+a,+d,+f,+m,+experimental-zihintntl,+zifencei" { +; CHECK-LABEL: test4 +; CHECL: .option pop +entry: + ret void +} + +; CHECK: .option push +; CHECK-NEXT: .option arch, +zifencei +define void @test5() "target-features"="+a,+d,+f,+m,+zifencei" { +; CHECK-LABEL: test5 +; CHECL: .option pop +entry: + ret void +}