diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1397,7 +1397,8 @@ /// Identify whether this target supports multiversioning of functions, /// which requires support for cpu_supports and cpu_is functionality. bool supportsMultiVersioning() const { - return getTriple().isX86() || getTriple().isAArch64(); + return getTriple().isX86() || getTriple().isAArch64() || + getTriple().isRISCV(); } /// Identify whether this target supports IFuncs. 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,8 @@ void fillValidCPUList(SmallVectorImpl &Values) const override; bool isValidTuneCPUName(StringRef Name) const override; void fillValidTuneCPUList(SmallVectorImpl &Values) const override; + 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); + + for (const std::string &Feature : ImpliedFeatures) + UpdatedFeatures.push_back(Feature); - return TargetInfo::initFeatureMap(Features, Diags, CPU, ImpliedFeatures); + return TargetInfo::initFeatureMap(Features, Diags, CPU, UpdatedFeatures); } std::optional> @@ -346,3 +351,78 @@ 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; + + auto SplitAndAddFeatures = [](StringRef FeatString, + std::vector &Features) { + SmallVector SplitFeatures; + FeatString.split(SplitFeatures, StringRef("+"), -1, false); + for (StringRef Feature : SplitFeatures) { + Features.push_back("+" + Feature.str()); + } + }; + + for (auto &Feature : AttrFeatures) { + Feature = Feature.trim(); + if (Feature.startswith("arch=")) { + if (FoundArch) + Ret.Duplicate = "arch="; + FoundArch = true; + std::pair Split = + Feature.split("=").second.trim().split("+"); + + if (!Split.first.empty()) { + auto RII = llvm::RISCVISAInfo::parseArchString( + Split.first, /* EnableExperimentalExtension */ true); + if (!RII) { + std::string Buffer; + llvm::raw_string_ostream OutputErrMsg(Buffer); + handleAllErrors(RII.takeError(), [&](llvm::StringError &ErrMsg) { + OutputErrMsg << "invalid arch name '" << Split.first << "', " + << ErrMsg.getMessage(); + }); + } + std::vector FeatStrings = (*RII)->toFeatureVector(); + for (auto FeatString : FeatStrings) + Ret.Features.push_back(FeatString); + } + + // Add any extra features, after the + + SplitAndAddFeatures(Split.second, Ret.Features); + } else if (Feature.startswith("cpu=")) { + if (!Ret.CPU.empty()) + Ret.Duplicate = "cpu="; + else { + // Split the cpu string into "cpu=", "cortex-a710" and any remaining + // "+feat" features. + std::pair Split = + Feature.split("=").second.trim().split("+"); + Ret.CPU = Split.first; + SplitAndAddFeatures(Split.second, Ret.Features); + } + } else if (Feature.startswith("tune=")) { + if (!Ret.Tune.empty()) + Ret.Duplicate = "tune="; + else + Ret.Tune = Feature.split("=").second.trim(); + } else if (Feature.startswith("+")) { + SplitAndAddFeatures(Feature, Ret.Features); + } + } + 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,35 @@ +// REQUIRES: riscv-registered-target +// RUN: %clang -target riscv64 -march=rv64g -S %s -o - | FileCheck %s + +// clang-format off +// CHECK: .option push +// CHECK-NEXT: .option arch, +c, +v, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +// CHECK-LABEL: test1 +// CHECK: .option pop +__attribute__((target("arch=rv64g+c+v"))) void test1 () {} + +// CHECK: .option push +// CHECK-NEXT: .option arch, +c +// CHECK-LABEL: test2 +// CHECK: .option pop +__attribute__((target("arch=rv64gc"))) void test2 () {} + +// CHECK: .option push +// CHECK-NEXT: .option arch, +c +// CHECK-LABEL: test3 +// CHECK: .option pop +__attribute__((target("arch=+c"))) void test3 () {} + +// CHECK: .option push +// CHECK-NEXT: .option arch, +v, +zve32f, +zve32x, +zve64d, +zve64f, +zve64x, +zvl128b, +zvl32b, +zvl64b +// CHECK-LABEL: test4 +// CHECK: .option pop +__attribute__((target("arch=+v"))) void test4 () {} + +// CHECK: .option push +// CHECK-NEXT: .option arch, +experimental-zihintntl +// CHECK-LABEL: test5 +// CHECK: .option pop +__attribute__((target("arch=+experimental-zihintntl"))) void test5 () {} + +// clang-format on 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 @@ -34,6 +34,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" @@ -44,6 +45,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; @@ -82,6 +87,8 @@ void emitEndOfAsmFile(Module &M) override; void emitFunctionEntryLabel() override; + void emitDirectiveOptionArch(); + bool isSameAttribute(); private: void emitAttributes(); @@ -232,11 +239,44 @@ return false; } +void RISCVAsmPrinter::emitDirectiveOptionArch() { + RISCVTargetStreamer &RTS = + static_cast(*OutStreamer->getTargetStreamer()); + std::vector NeedEmitStdOption; + const MCSubtargetInfo &MCSTI = *TM.getMCSubtargetInfo(); + for (auto Feature : RISCVFeatureKV) { + if (STI->hasFeature(Feature.Value) && !MCSTI.hasFeature(Feature.Value) && + llvm::RISCVISAInfo::isSupportedExtensionFeature(Feature.Key)) + NeedEmitStdOption.push_back(Feature.Key); + } + + bool PrefixEmitted = false; + unsigned NeedEmitStdOptionSize = NeedEmitStdOption.size(); + for (unsigned i = 0; i < NeedEmitStdOptionSize; i++) { + RTS.emitDirectiveOptionArchPlus(NeedEmitStdOption[i], PrefixEmitted, + i < NeedEmitStdOptionSize - 1); + } +} + +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; }