diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -669,7 +669,9 @@ // the input objects have been compiled. static void updateARMVFPArgs(const ARMAttributeParser &attributes, const InputFile *f) { - if (!attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args)) + Optional attr = + attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args); + if (!attr.hasValue()) // If an ABI tag isn't present then it is implicitly given the value of 0 // which maps to ARMBuildAttrs::BaseAAPCS. However many assembler files, // including some in glibc that don't use FP args (and should have value 3) @@ -677,7 +679,7 @@ // as a clash. return; - unsigned vfpArgs = attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args); + unsigned vfpArgs = attr.getValue(); ARMVFPArgKind arg; switch (vfpArgs) { case ARMBuildAttrs::BaseAAPCS: @@ -714,9 +716,11 @@ // is compiled with an architecture that supports these features then lld is // permitted to use them. static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) { - if (!attributes.hasAttribute(ARMBuildAttrs::CPU_arch)) + Optional attr = + attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); + if (!attr.hasValue()) return; - auto arch = attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); + auto arch = attr.getValue(); switch (arch) { case ARMBuildAttrs::Pre_v4: case ARMBuildAttrs::v4: diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -1239,7 +1239,7 @@ lldb::offset_t Offset = 0; uint8_t FormatVersion = data.GetU8(&Offset); - if (FormatVersion != llvm::ARMBuildAttrs::Format_Version) + if (FormatVersion != llvm::ELFAttrs::Format_Version) return; Offset = Offset + sizeof(uint32_t); // Section Length diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -881,6 +881,8 @@ SHT_MSP430_ATTRIBUTES = 0x70000003U, + SHT_RISCV_ATTRIBUTES = 0x70000003U, + SHT_HIPROC = 0x7fffffff, // Highest processor arch-specific type. SHT_LOUSER = 0x80000000, // Lowest type reserved for applications. SHT_HIUSER = 0xffffffff // Highest type reserved for applications. diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -28,8 +28,8 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" #include "llvm/Support/ARMAttributeParser.h" -#include "llvm/Support/ARMBuildAttributes.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/ELFAttributes.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" @@ -64,7 +64,7 @@ virtual uint64_t getSectionOffset(DataRefImpl Sec) const = 0; virtual Expected getRelocationAddend(DataRefImpl Rel) const = 0; - virtual Error getBuildAttributes(ARMAttributeParser &Attributes) const = 0; + virtual Error getBuildAttributes(ELFAttributeParser &Attributes) const = 0; public: using elf_symbol_iterator_range = iterator_range; @@ -365,19 +365,20 @@ (Visibility == ELF::STV_DEFAULT || Visibility == ELF::STV_PROTECTED)); } - Error getBuildAttributes(ARMAttributeParser &Attributes) const override { + Error getBuildAttributes(ELFAttributeParser &Attributes) const override { auto SectionsOrErr = EF.sections(); if (!SectionsOrErr) return SectionsOrErr.takeError(); for (const Elf_Shdr &Sec : *SectionsOrErr) { - if (Sec.sh_type == ELF::SHT_ARM_ATTRIBUTES) { + if (Sec.sh_type == ELF::SHT_ARM_ATTRIBUTES || + Sec.sh_type == ELF::SHT_RISCV_ATTRIBUTES) { auto ErrorOrContents = EF.getSectionContents(&Sec); if (!ErrorOrContents) return ErrorOrContents.takeError(); auto Contents = ErrorOrContents.get(); - if (Contents[0] != ARMBuildAttrs::Format_Version || Contents.size() == 1) + if (Contents[0] != ELFAttrs::Format_Version || Contents.size() == 1) return Error::success(); if (Error E = Attributes.parse(Contents, ELFT::TargetEndianness)) diff --git a/llvm/include/llvm/Support/ARMAttributeParser.h b/llvm/include/llvm/Support/ARMAttributeParser.h --- a/llvm/include/llvm/Support/ARMAttributeParser.h +++ b/llvm/include/llvm/Support/ARMAttributeParser.h @@ -1,4 +1,4 @@ -//===--- ARMAttributeParser.h - ARM Attribute Information Printer ---------===// +//===- ARMAttributeParser.h - ARM Attribute Information Printer -*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,36 +10,24 @@ #define LLVM_SUPPORT_ARMATTRIBUTEPARSER_H #include "ARMBuildAttributes.h" +#include "ELFAttributeParser.h" #include "ScopedPrinter.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" -#include - namespace llvm { class StringRef; -class ARMAttributeParser { - ScopedPrinter *sw; - - std::map attributes; - DataExtractor de{ArrayRef{}, true, 0}; - DataExtractor::Cursor cursor{0}; - +class ARMAttributeParser : public ELFAttributeParser { struct DisplayHandler { ARMBuildAttrs::AttrType attribute; Error (ARMAttributeParser::*routine)(ARMBuildAttrs::AttrType); }; static const DisplayHandler displayRoutines[]; - Error parseAttributeList(uint32_t length); - void parseIndexList(SmallVectorImpl &indexList); - Error parseSubsection(uint32_t length); - Error parseStringAttribute(const char *name, ARMBuildAttrs::AttrType tag, - const ArrayRef array); - void printAttribute(unsigned tag, unsigned value, StringRef valueDesc); + Error handler(uint64_t tag, bool &handled) override; Error stringAttribute(ARMBuildAttrs::AttrType tag); @@ -82,20 +70,11 @@ Error nodefaults(ARMBuildAttrs::AttrType tag); public: - ARMAttributeParser(ScopedPrinter *sw) : sw(sw) {} - ARMAttributeParser() : sw(nullptr) {} - ~ARMAttributeParser() { static_cast(!cursor.takeError()); } - - Error parse(ArrayRef section, support::endianness endian); - - bool hasAttribute(unsigned tag) const { return attributes.count(tag); } - - unsigned getAttributeValue(unsigned tag) const { - return attributes.find(tag)->second; - } + ARMAttributeParser(ScopedPrinter *sw) + : ELFAttributeParser(sw, ARMBuildAttrs::ARMAttributeTags, "aeabi") {} + ARMAttributeParser() + : ELFAttributeParser(ARMBuildAttrs::ARMAttributeTags, "aeabi") {} }; - } #endif - diff --git a/llvm/include/llvm/Support/ARMBuildAttributes.h b/llvm/include/llvm/Support/ARMBuildAttributes.h --- a/llvm/include/llvm/Support/ARMBuildAttributes.h +++ b/llvm/include/llvm/Support/ARMBuildAttributes.h @@ -18,18 +18,20 @@ #ifndef LLVM_SUPPORT_ARMBUILDATTRIBUTES_H #define LLVM_SUPPORT_ARMBUILDATTRIBUTES_H -namespace llvm { -class StringRef; +#include "llvm/Support/ELFAttributes.h" +namespace llvm { namespace ARMBuildAttrs { +extern const TagNameMap ARMAttributeTags; + enum SpecialAttr { // This is for the .cpu asm attr. It translates into one or more // AttrType (below) entries in the .ARM.attributes section in the ELF. SEL_CPU }; -enum AttrType { +enum AttrType : unsigned { // Rest correspond to ELF/.ARM.attributes File = 1, CPU_raw_name = 4, @@ -82,15 +84,6 @@ MPextension_use_old = 70 // recoded to MPextension_use (ABI r2.08) }; -StringRef AttrTypeAsString(unsigned Attr, bool HasTagPrefix = true); -StringRef AttrTypeAsString(AttrType Attr, bool HasTagPrefix = true); -int AttrTypeFromString(StringRef Tag); - -// Magic numbers for .ARM.attributes -enum AttrMagic { - Format_Version = 0x41 -}; - // Legal Values for CPU_arch, (=6), uleb128 enum CPUArch { Pre_v4 = 0, diff --git a/llvm/include/llvm/Support/ELFAttributeParser.h b/llvm/include/llvm/Support/ELFAttributeParser.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Support/ELFAttributeParser.h @@ -0,0 +1,72 @@ +//===- ELF AttributeParser.h - ELF Attribute Parser -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ELFATTRIBUTEPARSER_H +#define LLVM_SUPPORT_ELFATTRIBUTEPARSER_H + +#include "ELFAttributes.h" +#include "ScopedPrinter.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Error.h" + +#include + +namespace llvm { +class StringRef; + +class ELFAttributeParser { + StringRef vendor; + std::unordered_map attributes; + std::unordered_map attributesStr; + + virtual Error handler(uint64_t tag, bool &handled) = 0; + +protected: + ScopedPrinter *sw; + TagNameMap tagToStringMap; + DataExtractor de{ArrayRef{}, true, 0}; + DataExtractor::Cursor cursor{0}; + + void printAttribute(unsigned tag, unsigned value, StringRef valueDesc); + + Error parseStringAttribute(const char *name, unsigned tag, + ArrayRef strings); + Error parseAttributeList(uint32_t length); + void parseIndexList(SmallVectorImpl &indexList); + Error parseSubsection(uint32_t length); + +public: + virtual ~ELFAttributeParser() { static_cast(!cursor.takeError()); } + Error integerAttribute(unsigned tag); + Error stringAttribute(unsigned tag); + + ELFAttributeParser(ScopedPrinter *sw, TagNameMap tagNameMap, StringRef vendor) + : vendor(vendor), sw(sw), tagToStringMap(tagNameMap) {} + + ELFAttributeParser(TagNameMap tagNameMap, StringRef vendor) + : vendor(vendor), sw(nullptr), tagToStringMap(tagNameMap) {} + + Error parse(ArrayRef section, support::endianness endian); + + Optional getAttributeValue(unsigned tag) const { + auto I = attributes.find(tag); + if (I == attributes.end()) + return None; + return I->second; + } + Optional getAttributeString(unsigned tag) const { + auto I = attributesStr.find(tag); + if (I == attributesStr.end()) + return None; + return I->second; + } +}; + +} // namespace llvm +#endif diff --git a/llvm/include/llvm/Support/ELFAttributes.h b/llvm/include/llvm/Support/ELFAttributes.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Support/ELFAttributes.h @@ -0,0 +1,37 @@ +//===-- ELFAttributes.h - ELF Attributes ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ELFATTRIBUTES_H +#define LLVM_SUPPORT_ELFATTRIBUTES_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +namespace llvm { + +struct TagNameItem { + unsigned attr; + StringRef tagName; +}; + +using TagNameMap = ArrayRef; + +namespace ELFAttrs { + +enum AttrType : unsigned { File = 1, Section = 2, Symbol = 3 }; + +StringRef attrTypeAsString(unsigned attr, TagNameMap tagNameMap, + bool hasTagPrefix = true); +Optional attrTypeFromString(StringRef tag, TagNameMap tagNameMap); + +// Magic numbers for ELF attributes. +enum AttrMagic { Format_Version = 0x41 }; + +} // namespace ELFAttrs +} // namespace llvm +#endif diff --git a/llvm/include/llvm/Support/RISCVAttributeParser.h b/llvm/include/llvm/Support/RISCVAttributeParser.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Support/RISCVAttributeParser.h @@ -0,0 +1,38 @@ +//===-- RISCVAttributeParser.h - RISCV Attribute Parser ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_RISCVATTRIBUTEPARSER_H +#define LLVM_SUPPORT_RISCVATTRIBUTEPARSER_H + +#include "ScopedPrinter.h" +#include "llvm/Support/ELFAttributeParser.h" +#include "llvm/Support/RISCVAttributes.h" + +namespace llvm { +class RISCVAttributeParser : public ELFAttributeParser { + struct DisplayHandler { + RISCVAttrs::AttrType attribute; + Error (RISCVAttributeParser::*routine)(unsigned); + }; + static const DisplayHandler displayRoutines[]; + + Error handler(uint64_t tag, bool &handled) override; + + Error unalignedAccess(unsigned tag); + Error stackAlign(unsigned tag); + +public: + RISCVAttributeParser(ScopedPrinter *sw) + : ELFAttributeParser(sw, RISCVAttrs::RISCVAttributeTags, "riscv") {} + RISCVAttributeParser() + : ELFAttributeParser(RISCVAttrs::RISCVAttributeTags, "riscv") {} +}; + +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/Support/RISCVAttributes.h b/llvm/include/llvm/Support/RISCVAttributes.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Support/RISCVAttributes.h @@ -0,0 +1,44 @@ +//===-- RISCVAttributes.h - RISCV Attributes --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains enumerations for RISCV attributes as defined in RISC-V +// ELF psABI specification. +// +// RISC-V ELF psABI specification +// +// https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SUPPORT_RISCVATTRIBUTES_H +#define LLVM_SUPPORT_RISCVATTRIBUTES_H + +#include "llvm/Support/ELFAttributes.h" + +namespace llvm { +namespace RISCVAttrs { + +extern const TagNameMap RISCVAttributeTags; + +enum AttrType : unsigned { + // Attribute types in ELF/.riscv.attributes. + STACK_ALIGN = 4, + ARCH = 5, + UNALIGNED_ACCESS = 6, + PRIV_SPEC = 8, + PRIV_SPEC_MINOR = 10, + PRIV_SPEC_REVISION = 12, +}; + +enum StackAlign { ALIGN_4 = 4, ALIGN_16 = 16 }; + +enum { NOT_ALLOWED = 0, ALLOWED = 1 }; + +} // namespace RISCVAttrs +} // namespace llvm + +#endif diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -223,6 +223,9 @@ STRINGIFY_ENUM_CASE(ELF, SHT_MIPS_ABIFLAGS); } break; + case ELF::EM_RISCV: + switch (Type) { STRINGIFY_ENUM_CASE(ELF, SHT_RISCV_ATTRIBUTES); } + break; default: break; } diff --git a/llvm/lib/Object/ELFObjectFile.cpp b/llvm/lib/Object/ELFObjectFile.cpp --- a/llvm/lib/Object/ELFObjectFile.cpp +++ b/llvm/lib/Object/ELFObjectFile.cpp @@ -23,6 +23,8 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/RISCVAttributeParser.h" +#include "llvm/Support/RISCVAttributes.h" #include "llvm/Support/TargetRegistry.h" #include #include @@ -164,12 +166,14 @@ // both ARMv7-M and R have to support thumb hardware div bool isV7 = false; - if (Attributes.hasAttribute(ARMBuildAttrs::CPU_arch)) - isV7 = Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch) - == ARMBuildAttrs::v7; - - if (Attributes.hasAttribute(ARMBuildAttrs::CPU_arch_profile)) { - switch(Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch_profile)) { + Optional Attr = + Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); + if (Attr.hasValue()) + isV7 = Attr.getValue() == ARMBuildAttrs::v7; + + Attr = Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch_profile); + if (Attr.hasValue()) { + switch (Attr.getValue()) { case ARMBuildAttrs::ApplicationProfile: Features.AddFeature("aclass"); break; @@ -186,8 +190,9 @@ } } - if (Attributes.hasAttribute(ARMBuildAttrs::THUMB_ISA_use)) { - switch(Attributes.getAttributeValue(ARMBuildAttrs::THUMB_ISA_use)) { + Attr = Attributes.getAttributeValue(ARMBuildAttrs::THUMB_ISA_use); + if (Attr.hasValue()) { + switch (Attr.getValue()) { default: break; case ARMBuildAttrs::Not_Allowed: @@ -200,8 +205,9 @@ } } - if (Attributes.hasAttribute(ARMBuildAttrs::FP_arch)) { - switch(Attributes.getAttributeValue(ARMBuildAttrs::FP_arch)) { + Attr = Attributes.getAttributeValue(ARMBuildAttrs::FP_arch); + if (Attr.hasValue()) { + switch (Attr.getValue()) { default: break; case ARMBuildAttrs::Not_Allowed: @@ -223,8 +229,9 @@ } } - if (Attributes.hasAttribute(ARMBuildAttrs::Advanced_SIMD_arch)) { - switch(Attributes.getAttributeValue(ARMBuildAttrs::Advanced_SIMD_arch)) { + Attr = Attributes.getAttributeValue(ARMBuildAttrs::Advanced_SIMD_arch); + if (Attr.hasValue()) { + switch (Attr.getValue()) { default: break; case ARMBuildAttrs::Not_Allowed: @@ -241,8 +248,9 @@ } } - if (Attributes.hasAttribute(ARMBuildAttrs::MVE_arch)) { - switch(Attributes.getAttributeValue(ARMBuildAttrs::MVE_arch)) { + Attr = Attributes.getAttributeValue(ARMBuildAttrs::MVE_arch); + if (Attr.hasValue()) { + switch (Attr.getValue()) { default: break; case ARMBuildAttrs::Not_Allowed: @@ -259,8 +267,9 @@ } } - if (Attributes.hasAttribute(ARMBuildAttrs::DIV_use)) { - switch(Attributes.getAttributeValue(ARMBuildAttrs::DIV_use)) { + Attr = Attributes.getAttributeValue(ARMBuildAttrs::DIV_use); + if (Attr.hasValue()) { + switch (Attr.getValue()) { default: break; case ARMBuildAttrs::DisallowDIV: @@ -285,6 +294,48 @@ Features.AddFeature("c"); } + // Add features according to the ELF attribute section. + // If there are any unrecognized features, ignore them. + RISCVAttributeParser Attributes; + if (Error E = getBuildAttributes(Attributes)) + return Features; // Keep "c" feature if there is one in PlatformFlags. + + Optional Attr = Attributes.getAttributeString(RISCVAttrs::ARCH); + if (Attr.hasValue()) { + // The Arch pattern is [rv32|rv64][i|e]version(_[m|a|f|d|c]version)* + // Version string pattern is (major)p(minor). Major and minor are optional. + // For example, a version number could be 2p0, 2, or p92. + StringRef Arch = Attr.getValue(); + if (Arch.consume_front("rv32")) + Features.AddFeature("64bit", false); + else if (Arch.consume_front("rv64")) + Features.AddFeature("64bit"); + + while (!Arch.empty()) { + switch (Arch[0]) { + default: + break; // Ignore unexpected features. + case 'i': + Features.AddFeature("e", false); + break; + case 'd': + Features.AddFeature("f"); // D-ext will imply F-ext. + LLVM_FALLTHROUGH; + case 'e': + case 'm': + case 'a': + case 'f': + case 'c': + Features.AddFeature(Arch.take_front()); + break; + } + + // FIXME: Handle version numbers. + Arch = Arch.drop_until([](char c) { return c == '_' || c == '\0'; }); + Arch = Arch.drop_while([](char c) { return c == '_'; }); + } + } + return Features; } @@ -320,8 +371,10 @@ else Triple = "arm"; - if (Attributes.hasAttribute(ARMBuildAttrs::CPU_arch)) { - switch(Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch)) { + Optional Attr = + Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch); + if (Attr.hasValue()) { + switch (Attr.getValue()) { case ARMBuildAttrs::v4: Triple += "v4"; break; diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -500,6 +500,9 @@ ECase(SHT_MIPS_DWARF); ECase(SHT_MIPS_ABIFLAGS); break; + case ELF::EM_RISCV: + ECase(SHT_RISCV_ATTRIBUTES); + break; default: // Nothing to do. break; diff --git a/llvm/lib/Support/ARMAttributeParser.cpp b/llvm/lib/Support/ARMAttributeParser.cpp --- a/llvm/lib/Support/ARMAttributeParser.cpp +++ b/llvm/lib/Support/ARMAttributeParser.cpp @@ -1,4 +1,4 @@ -//===--- ARMAttributeParser.cpp - ARM Attribute Information Printer -------===// +//===- ARMAttributeParser.cpp - ARM Attribute Information Printer ---------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -16,12 +16,6 @@ using namespace llvm; using namespace llvm::ARMBuildAttrs; -static const EnumEntry tagNames[] = { - {"Tag_File", ARMBuildAttrs::File}, - {"Tag_Section", ARMBuildAttrs::Section}, - {"Tag_Symbol", ARMBuildAttrs::Symbol}, -}; - #define ATTRIBUTE_HANDLER(attr) \ { ARMBuildAttrs::attr, &ARMAttributeParser::attr } @@ -71,7 +65,8 @@ #undef ATTRIBUTE_HANDLER Error ARMAttributeParser::stringAttribute(AttrType tag) { - StringRef tagName = ARMBuildAttrs::AttrTypeAsString(tag, /*TagPrefix=*/false); + StringRef tagName = + ELFAttrs::attrTypeAsString(tag, tagToStringMap, /*TagPrefix=*/false); StringRef desc = de.getCStrRef(cursor); if (sw) { @@ -84,36 +79,6 @@ return Error::success(); } -void ARMAttributeParser::printAttribute(unsigned tag, unsigned value, - StringRef valueDesc) { - attributes.insert(std::make_pair(tag, value)); - - if (sw) { - StringRef tagName = - ARMBuildAttrs::AttrTypeAsString(tag, /*TagPrefix=*/false); - DictScope as(*sw, "Attribute"); - sw->printNumber("Tag", tag); - sw->printNumber("Value", value); - if (!tagName.empty()) - sw->printString("TagName", tagName); - if (!valueDesc.empty()) - sw->printString("Description", valueDesc); - } -} - -Error ARMAttributeParser::parseStringAttribute(const char *name, AttrType tag, - ArrayRef strings) { - uint64_t value = de.getULEB128(cursor); - if (value >= strings.size()) { - printAttribute(tag, value, ""); - return createStringError(errc::invalid_argument, - "unknown " + Twine(name) + - " value: " + Twine(value)); - } - printAttribute(tag, value, strings[value]); - return Error::success(); -} - Error ARMAttributeParser::CPU_arch(AttrType tag) { static const char *strings[] = { "Pre-v4", "ARM v4", "ARM v4T", "ARM v5T", "ARM v5TE", "ARM v5TEJ", "ARM v6", @@ -323,7 +288,9 @@ DictScope scope(*sw, "Attribute"); sw->printNumber("Tag", tag); sw->startLine() << "Value: " << integer << ", " << string << '\n'; - sw->printString("TagName", AttrTypeAsString(tag, /*TagPrefix*/ false)); + sw->printString("TagName", + ELFAttrs::attrTypeAsString(tag, tagToStringMap, + /*hasTagPrefix=*/false)); switch (integer) { case 0: sw->printString("Description", StringRef("No Specific Requirements")); @@ -389,167 +356,18 @@ return Error::success(); } -void ARMAttributeParser::parseIndexList(SmallVectorImpl &indexList) { - for (;;) { - uint64_t value = de.getULEB128(cursor); - if (!cursor || !value) +Error ARMAttributeParser::handler(uint64_t tag, bool &handled) { + handled = false; + for (unsigned AHI = 0, AHE = array_lengthof(displayRoutines); AHI != AHE; + ++AHI) { + if (uint64_t(displayRoutines[AHI].attribute) == tag) { + if (Error e = + (this->*displayRoutines[AHI].routine)(static_cast(tag))) + return e; + handled = true; break; - indexList.push_back(value); - } -} - -Error ARMAttributeParser::parseAttributeList(uint32_t length) { - uint64_t pos; - uint64_t end = cursor.tell() + length; - while ((pos = cursor.tell()) < end) { - uint64_t tag = de.getULEB128(cursor); - bool handled = false; - for (unsigned AHI = 0, AHE = array_lengthof(displayRoutines); - AHI != AHE && !handled; ++AHI) { - if (uint64_t(displayRoutines[AHI].attribute) == tag) { - if (Error e = (this->*displayRoutines[AHI].routine)( - ARMBuildAttrs::AttrType(tag))) - return e; - handled = true; - break; - } - } - if (!handled) { - if (tag < 32) - return createStringError(errc::invalid_argument, - "invalid AEABI tag 0x" + - Twine::utohexstr(tag) + " at offset 0x" + - Twine::utohexstr(pos)); - - if (tag % 2 == 0) { - uint64_t value = de.getULEB128(cursor); - attributes.insert(std::make_pair(tag, value)); - if (sw) - sw->printNumber(ARMBuildAttrs::AttrTypeAsString(tag), value); - } else { - StringRef tagName = - ARMBuildAttrs::AttrTypeAsString(tag, /*TagPrefix=*/false); - StringRef desc = de.getCStrRef(cursor); - - if (sw) { - DictScope scope(*sw, "Attribute"); - sw->printNumber("Tag", tag); - if (!tagName.empty()) - sw->printString("TagName", tagName); - sw->printString("Value", desc); - } - } } } - return Error::success(); -} - -Error ARMAttributeParser::parseSubsection(uint32_t length) { - uint64_t end = cursor.tell() - sizeof(length) + length; - StringRef vendorName = de.getCStrRef(cursor); - if (sw) { - sw->printNumber("SectionLength", length); - sw->printString("Vendor", vendorName); - } - - // Ignore unrecognized vendor-name. - if (vendorName.lower() != "aeabi") - return createStringError(errc::invalid_argument, - "unrecognized vendor-name: " + vendorName); - - while (cursor.tell() < end) { - /// Tag_File | Tag_Section | Tag_Symbol uleb128:byte-size - uint8_t tag = de.getU8(cursor); - uint32_t size = de.getU32(cursor); - if (!cursor) - return cursor.takeError(); - - if (sw) { - sw->printEnum("Tag", tag, makeArrayRef(tagNames)); - sw->printNumber("Size", size); - } - if (size < 5) - return createStringError(errc::invalid_argument, - "invalid attribute size " + Twine(size) + - " at offset 0x" + - Twine::utohexstr(cursor.tell() - 5)); - - StringRef scopeName, indexName; - SmallVector indicies; - switch (tag) { - case ARMBuildAttrs::File: - scopeName = "FileAttributes"; - break; - case ARMBuildAttrs::Section: - scopeName = "SectionAttributes"; - indexName = "Sections"; - parseIndexList(indicies); - break; - case ARMBuildAttrs::Symbol: - scopeName = "SymbolAttributes"; - indexName = "Symbols"; - parseIndexList(indicies); - break; - default: - return createStringError(errc::invalid_argument, - "unrecognized tag 0x" + Twine::utohexstr(tag) + - " at offset 0x" + - Twine::utohexstr(cursor.tell() - 5)); - } - if (sw) { - DictScope scope(*sw, scopeName); - if (!indicies.empty()) - sw->printList(indexName, indicies); - if (Error e = parseAttributeList(size - 5)) - return e; - } else if (Error e = parseAttributeList(size - 5)) - return e; - } return Error::success(); } - -Error ARMAttributeParser::parse(ArrayRef section, - support::endianness endian) { - unsigned sectionNumber = 0; - de = DataExtractor(section, endian == support::little, 0); - - // For early returns, we have more specific errors, consume the Error in - // cursor. - struct ClearCursorError { - DataExtractor::Cursor &cursor; - ~ClearCursorError() { consumeError(cursor.takeError()); } - } clear{cursor}; - - // Unrecognized format-version. - uint8_t formatVersion = de.getU8(cursor); - if (formatVersion != 'A') - return createStringError(errc::invalid_argument, - "unrecognized format-version: 0x" + - utohexstr(formatVersion)); - - while (!de.eof(cursor)) { - uint32_t sectionLength = de.getU32(cursor); - if (!cursor) - return cursor.takeError(); - - if (sw) { - sw->startLine() << "Section " << ++sectionNumber << " {\n"; - sw->indent(); - } - - if (sectionLength < 4 || cursor.tell() - 4 + sectionLength > section.size()) - return createStringError(errc::invalid_argument, - "invalid subsection length " + - Twine(sectionLength) + " at offset 0x" + - utohexstr(cursor.tell() - 4)); - if (Error e = parseSubsection(sectionLength)) - return e; - if (sw) { - sw->unindent(); - sw->startLine() << "}\n"; - } - } - - return cursor.takeError(); -} diff --git a/llvm/lib/Support/ARMBuildAttrs.cpp b/llvm/lib/Support/ARMBuildAttrs.cpp --- a/llvm/lib/Support/ARMBuildAttrs.cpp +++ b/llvm/lib/Support/ARMBuildAttrs.cpp @@ -6,16 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/StringRef.h" #include "llvm/Support/ARMBuildAttributes.h" using namespace llvm; -namespace { -const struct { - ARMBuildAttrs::AttrType Attr; - StringRef TagName; -} ARMAttributeTags[] = { +static const TagNameItem tagData[] = { {ARMBuildAttrs::File, "Tag_File"}, {ARMBuildAttrs::Section, "Tag_Section"}, {ARMBuildAttrs::Symbol, "Tag_Symbol"}, @@ -67,35 +62,7 @@ {ARMBuildAttrs::ABI_align_needed, "Tag_ABI_align8_needed"}, {ARMBuildAttrs::ABI_align_preserved, "Tag_ABI_align8_preserved"}, }; -} -namespace llvm { -namespace ARMBuildAttrs { -StringRef AttrTypeAsString(unsigned Attr, bool HasTagPrefix) { - return AttrTypeAsString(static_cast(Attr), HasTagPrefix); -} - -StringRef AttrTypeAsString(AttrType Attr, bool HasTagPrefix) { - for (unsigned TI = 0, TE = sizeof(ARMAttributeTags) / sizeof(*ARMAttributeTags); - TI != TE; ++TI) - if (ARMAttributeTags[TI].Attr == Attr) { - auto TagName = ARMAttributeTags[TI].TagName; - return HasTagPrefix ? TagName : TagName.drop_front(4); - } - return ""; -} - -int AttrTypeFromString(StringRef Tag) { - bool HasTagPrefix = Tag.startswith("Tag_"); - for (unsigned TI = 0, - TE = sizeof(ARMAttributeTags) / sizeof(*ARMAttributeTags); - TI != TE; ++TI) { - auto TagName = ARMAttributeTags[TI].TagName; - if (TagName.drop_front(HasTagPrefix ? 0 : 4) == Tag) { - return ARMAttributeTags[TI].Attr; - } - } - return -1; -} -} -} +const TagNameMap llvm::ARMBuildAttrs::ARMAttributeTags(tagData, + sizeof(tagData) / + sizeof(TagNameItem)); diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -87,6 +87,8 @@ DeltaAlgorithm.cpp DAGDeltaAlgorithm.cpp DJB.cpp + ELFAttributeParser.cpp + ELFAttributes.cpp Error.cpp ErrorHandling.cpp FileCheck.cpp @@ -122,6 +124,8 @@ PrettyStackTrace.cpp RandomNumberGenerator.cpp Regex.cpp + RISCVAttributes.cpp + RISCVAttributeParser.cpp ScaledNumber.cpp ScopedPrinter.cpp SHA1.cpp diff --git a/llvm/lib/Support/ELFAttributeParser.cpp b/llvm/lib/Support/ELFAttributeParser.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Support/ELFAttributeParser.cpp @@ -0,0 +1,233 @@ +//===--- ELFAttributeParser.cpp - ELF Attribute Parser --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/ELFAttributeParser.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::ELFAttrs; + +static const EnumEntry tagNames[] = { + {"Tag_File", ELFAttrs::File}, + {"Tag_Section", ELFAttrs::Section}, + {"Tag_Symbol", ELFAttrs::Symbol}, +}; + +Error ELFAttributeParser::parseStringAttribute(const char *name, unsigned tag, + ArrayRef strings) { + uint64_t value = de.getULEB128(cursor); + if (value >= strings.size()) { + printAttribute(tag, value, ""); + return createStringError(errc::invalid_argument, + "unknown " + Twine(name) + + " value: " + Twine(value)); + } + printAttribute(tag, value, strings[value]); + return Error::success(); +} + +Error ELFAttributeParser::integerAttribute(unsigned tag) { + StringRef tagName = + ELFAttrs::attrTypeAsString(tag, tagToStringMap, /*hasTagPrefix=*/false); + uint64_t value = de.getULEB128(cursor); + attributes.insert(std::make_pair(tag, value)); + + if (sw) { + DictScope scope(*sw, "Attribute"); + sw->printNumber("Tag", tag); + if (!tagName.empty()) + sw->printString("TagName", tagName); + sw->printNumber("Value", value); + } + return Error::success(); +} + +Error ELFAttributeParser::stringAttribute(unsigned tag) { + StringRef tagName = + ELFAttrs::attrTypeAsString(tag, tagToStringMap, /*hasTagPrefix=*/false); + StringRef desc = de.getCStrRef(cursor); + attributesStr.insert(std::make_pair(tag, desc)); + + if (sw) { + DictScope scope(*sw, "Attribute"); + sw->printNumber("Tag", tag); + if (!tagName.empty()) + sw->printString("TagName", tagName); + sw->printString("Value", desc); + } + return Error::success(); +} + +void ELFAttributeParser::printAttribute(unsigned tag, unsigned value, + StringRef valueDesc) { + attributes.insert(std::make_pair(tag, value)); + + if (sw) { + StringRef tagName = ELFAttrs::attrTypeAsString(tag, tagToStringMap, + /*hasTagPrefix=*/false); + DictScope as(*sw, "Attribute"); + sw->printNumber("Tag", tag); + sw->printNumber("Value", value); + if (!tagName.empty()) + sw->printString("TagName", tagName); + if (!valueDesc.empty()) + sw->printString("Description", valueDesc); + } +} + +void ELFAttributeParser::parseIndexList(SmallVectorImpl &indexList) { + for (;;) { + uint64_t value = de.getULEB128(cursor); + if (!cursor || !value) + break; + indexList.push_back(value); + } +} + +Error ELFAttributeParser::parseAttributeList(uint32_t length) { + uint64_t pos; + uint64_t end = cursor.tell() + length; + while ((pos = cursor.tell()) < end) { + uint64_t tag = de.getULEB128(cursor); + bool handled; + if (Error e = handler(tag, handled)) + return e; + + if (!handled) { + if (tag < 32) { + return createStringError(errc::invalid_argument, + "invalid tag 0x" + Twine::utohexstr(tag) + + " at offset 0x" + Twine::utohexstr(pos)); + } + + if (tag % 2 == 0) { + if (Error e = integerAttribute(tag)) + return e; + } else { + if (Error e = stringAttribute(tag)) + return e; + } + } + } + return Error::success(); +} + +Error ELFAttributeParser::parseSubsection(uint32_t length) { + uint64_t end = cursor.tell() - sizeof(length) + length; + StringRef vendorName = de.getCStrRef(cursor); + if (sw) { + sw->printNumber("SectionLength", length); + sw->printString("Vendor", vendorName); + } + + // Ignore unrecognized vendor-name. + if (vendorName.lower() != vendor) + return createStringError(errc::invalid_argument, + "unrecognized vendor-name: " + vendorName); + + while (cursor.tell() < end) { + /// Tag_File | Tag_Section | Tag_Symbol uleb128:byte-size + uint8_t tag = de.getU8(cursor); + uint32_t size = de.getU32(cursor); + if (!cursor) + return cursor.takeError(); + + if (sw) { + sw->printEnum("Tag", tag, makeArrayRef(tagNames)); + sw->printNumber("Size", size); + } + if (size < 5) + return createStringError(errc::invalid_argument, + "invalid attribute size " + Twine(size) + + " at offset 0x" + + Twine::utohexstr(cursor.tell() - 5)); + + StringRef scopeName, indexName; + SmallVector indicies; + switch (tag) { + case ELFAttrs::File: + scopeName = "FileAttributes"; + break; + case ELFAttrs::Section: + scopeName = "SectionAttributes"; + indexName = "Sections"; + parseIndexList(indicies); + break; + case ELFAttrs::Symbol: + scopeName = "SymbolAttributes"; + indexName = "Symbols"; + parseIndexList(indicies); + break; + default: + return createStringError(errc::invalid_argument, + "unrecognized tag 0x" + Twine::utohexstr(tag) + + " at offset 0x" + + Twine::utohexstr(cursor.tell() - 5)); + } + + if (sw) { + DictScope scope(*sw, scopeName); + if (!indicies.empty()) + sw->printList(indexName, indicies); + if (Error e = parseAttributeList(size - 5)) + return e; + } else if (Error e = parseAttributeList(size - 5)) + return e; + } + return Error::success(); +} + +Error ELFAttributeParser::parse(ArrayRef section, + support::endianness endian) { + unsigned sectionNumber = 0; + de = DataExtractor(section, endian == support::little, 0); + + // For early returns, we have more specific errors, consume the Error in + // cursor. + struct ClearCursorError { + DataExtractor::Cursor &cursor; + ~ClearCursorError() { consumeError(cursor.takeError()); } + } clear{cursor}; + + // Unrecognized format-version. + uint8_t formatVersion = de.getU8(cursor); + if (formatVersion != 'A') + return createStringError(errc::invalid_argument, + "unrecognized format-version: 0x" + + utohexstr(formatVersion)); + + while (!de.eof(cursor)) { + uint32_t sectionLength = de.getU32(cursor); + if (!cursor) + return cursor.takeError(); + + if (sw) { + sw->startLine() << "Section " << ++sectionNumber << " {\n"; + sw->indent(); + } + + if (sectionLength < 4 || cursor.tell() - 4 + sectionLength > section.size()) + return createStringError(errc::invalid_argument, + "invalid subsection length " + + Twine(sectionLength) + " at offset 0x" + + utohexstr(cursor.tell() - 4)); + + if (Error e = parseSubsection(sectionLength)) + return e; + if (sw) { + sw->unindent(); + sw->startLine() << "}\n"; + } + } + + return cursor.takeError(); +} diff --git a/llvm/lib/Support/ELFAttributes.cpp b/llvm/lib/Support/ELFAttributes.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Support/ELFAttributes.cpp @@ -0,0 +1,34 @@ +//===-- ELFAttributes.cpp - ELF Attributes --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/ELFAttributes.h" +#include "llvm/ADT/StringRef.h" + +using namespace llvm; + +StringRef ELFAttrs::attrTypeAsString(unsigned attr, TagNameMap tagNameMap, + bool hasTagPrefix) { + auto tagNameIt = find_if( + tagNameMap, [attr](const TagNameItem item) { return item.attr == attr; }); + if (tagNameIt == tagNameMap.end()) + return ""; + StringRef tagName = tagNameIt->tagName; + return hasTagPrefix ? tagName : tagName.drop_front(4); +} + +Optional ELFAttrs::attrTypeFromString(StringRef tag, + TagNameMap tagNameMap) { + bool hasTagPrefix = tag.startswith("Tag_"); + auto tagNameIt = + find_if(tagNameMap, [tag, hasTagPrefix](const TagNameItem item) { + return item.tagName.drop_front(hasTagPrefix ? 0 : 4) == tag; + }); + if (tagNameIt == tagNameMap.end()) + return None; + return tagNameIt->attr; +} diff --git a/llvm/lib/Support/RISCVAttributeParser.cpp b/llvm/lib/Support/RISCVAttributeParser.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Support/RISCVAttributeParser.cpp @@ -0,0 +1,67 @@ +//===-- RISCVAttributeParser.cpp - RISCV Attribute Parser -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/RISCVAttributeParser.h" +#include "llvm/ADT/StringExtras.h" + +using namespace llvm; + +const RISCVAttributeParser::DisplayHandler + RISCVAttributeParser::displayRoutines[] = { + { + RISCVAttrs::ARCH, + &ELFAttributeParser::stringAttribute, + }, + { + RISCVAttrs::PRIV_SPEC, + &ELFAttributeParser::integerAttribute, + }, + { + RISCVAttrs::PRIV_SPEC_MINOR, + &ELFAttributeParser::integerAttribute, + }, + { + RISCVAttrs::PRIV_SPEC_REVISION, + &ELFAttributeParser::integerAttribute, + }, + { + RISCVAttrs::STACK_ALIGN, + &RISCVAttributeParser::stackAlign, + }, + { + RISCVAttrs::UNALIGNED_ACCESS, + &RISCVAttributeParser::unalignedAccess, + }}; + +Error RISCVAttributeParser::unalignedAccess(unsigned tag) { + static const char *strings[] = {"No unaligned access", "Unaligned access"}; + return parseStringAttribute("Unaligned_access", tag, makeArrayRef(strings)); +} + +Error RISCVAttributeParser::stackAlign(unsigned tag) { + uint64_t value = de.getULEB128(cursor); + std::string description = + "Stack alignment is " + utostr(value) + std::string("-bytes"); + printAttribute(tag, value, description); + return Error::success(); +} + +Error RISCVAttributeParser::handler(uint64_t tag, bool &handled) { + handled = false; + for (unsigned AHI = 0, AHE = array_lengthof(displayRoutines); AHI != AHE; + ++AHI) { + if (uint64_t(displayRoutines[AHI].attribute) == tag) { + if (Error e = (this->*displayRoutines[AHI].routine)(tag)) + return e; + handled = true; + break; + } + } + + return Error::success(); +} diff --git a/llvm/lib/Support/RISCVAttributes.cpp b/llvm/lib/Support/RISCVAttributes.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Support/RISCVAttributes.cpp @@ -0,0 +1,25 @@ +//===-- RISCVAttributes.cpp - RISCV Attributes ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/RISCVAttributes.h" + +using namespace llvm; +using namespace llvm::RISCVAttrs; + +static const TagNameItem tagData[] = { + {STACK_ALIGN, "Tag_stack_align"}, + {ARCH, "Tag_arch"}, + {UNALIGNED_ACCESS, "Tag_unaligned_access"}, + {PRIV_SPEC, "Tag_priv_spec"}, + {PRIV_SPEC_MINOR, "Tag_priv_spec_minor"}, + {PRIV_SPEC_REVISION, "Tag_priv_spec_revision"}, +}; + +const TagNameMap llvm::RISCVAttrs::RISCVAttributeTags(tagData, + sizeof(tagData) / + sizeof(TagNameItem)); diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp --- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -11143,11 +11143,13 @@ TagLoc = Parser.getTok().getLoc(); if (Parser.getTok().is(AsmToken::Identifier)) { StringRef Name = Parser.getTok().getIdentifier(); - Tag = ARMBuildAttrs::AttrTypeFromString(Name); - if (Tag == -1) { + Optional Ret = + ELFAttrs::attrTypeFromString(Name, ARMBuildAttrs::ARMAttributeTags); + if (!Ret.hasValue()) { Error(TagLoc, "attribute name not recognised: " + Name); return false; } + Tag = Ret.getValue(); Parser.Lex(); } else { const MCExpr *AttrExpr; diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp @@ -177,7 +177,8 @@ void ARMTargetAsmStreamer::emitAttribute(unsigned Attribute, unsigned Value) { OS << "\t.eabi_attribute\t" << Attribute << ", " << Twine(Value); if (IsVerboseAsm) { - StringRef Name = ARMBuildAttrs::AttrTypeAsString(Attribute); + StringRef Name = + ELFAttrs::attrTypeAsString(Attribute, ARMBuildAttrs::ARMAttributeTags); if (!Name.empty()) OS << "\t@ " << Name; } @@ -193,7 +194,8 @@ default: OS << "\t.eabi_attribute\t" << Attribute << ", \"" << String << "\""; if (IsVerboseAsm) { - StringRef Name = ARMBuildAttrs::AttrTypeAsString(Attribute); + StringRef Name = ELFAttrs::attrTypeAsString( + Attribute, ARMBuildAttrs::ARMAttributeTags); if (!Name.empty()) OS << "\t@ " << Name; } @@ -212,7 +214,9 @@ if (!StringValue.empty()) OS << ", \"" << StringValue << "\""; if (IsVerboseAsm) - OS << "\t@ " << ARMBuildAttrs::AttrTypeAsString(Attribute); + OS << "\t@ " + << ELFAttrs::attrTypeAsString(Attribute, + ARMBuildAttrs::ARMAttributeTags); break; } OS << "\n"; diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp --- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -32,6 +32,7 @@ #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/Casting.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/RISCVAttributes.h" #include "llvm/Support/TargetRegistry.h" #include @@ -146,6 +147,7 @@ bool parseOperand(OperandVector &Operands, StringRef Mnemonic); bool parseDirectiveOption(); + bool parseDirectiveAttribute(); void setFeatureBits(uint64_t Feature, StringRef FeatureString) { if (!(getSTI().getFeatureBits()[Feature])) { @@ -155,6 +157,10 @@ } } + bool getFeatureBits(uint64_t Feature) { + return getSTI().getFeatureBits()[Feature]; + } + void clearFeatureBits(uint64_t Feature, StringRef FeatureString) { if (getSTI().getFeatureBits()[Feature]) { MCSubtargetInfo &STI = copySTI(); @@ -1579,6 +1585,8 @@ if (IDVal == ".option") return parseDirectiveOption(); + else if (IDVal == ".attribute") + return parseDirectiveAttribute(); return true; } @@ -1677,6 +1685,151 @@ return false; } +/// parseDirectiveAttribute +/// ::= .attribute expression ',' ( expression | "string" ) +/// ::= .attribute identifier ',' ( expression | "string" ) +bool RISCVAsmParser::parseDirectiveAttribute() { + MCAsmParser &Parser = getParser(); + int64_t Tag; + SMLoc TagLoc; + TagLoc = Parser.getTok().getLoc(); + if (Parser.getTok().is(AsmToken::Identifier)) { + StringRef Name = Parser.getTok().getIdentifier(); + Optional Ret = + ELFAttrs::attrTypeFromString(Name, RISCVAttrs::RISCVAttributeTags); + if (!Ret.hasValue()) { + Error(TagLoc, "attribute name not recognised: " + Name); + return false; + } + Tag = Ret.getValue(); + Parser.Lex(); + } else { + const MCExpr *AttrExpr; + + TagLoc = Parser.getTok().getLoc(); + if (Parser.parseExpression(AttrExpr)) + return true; + + const MCConstantExpr *CE = dyn_cast(AttrExpr); + if (check(!CE, TagLoc, "expected numeric constant")) + return true; + + Tag = CE->getValue(); + } + + if (Parser.parseToken(AsmToken::Comma, "comma expected")) + return true; + + StringRef StringValue; + int64_t IntegerValue = 0; + bool IsIntegerValue = true; + + // RISC-V attributes have a string value if the tag number is odd + // and an integer value if the tag number is even. + if (Tag % 2) + IsIntegerValue = false; + + SMLoc ValueExprLoc = Parser.getTok().getLoc(); + if (IsIntegerValue) { + const MCExpr *ValueExpr; + if (Parser.parseExpression(ValueExpr)) + return true; + + const MCConstantExpr *CE = dyn_cast(ValueExpr); + if (!CE) + return Error(ValueExprLoc, "expected numeric constant"); + IntegerValue = CE->getValue(); + } else { + if (Parser.getTok().isNot(AsmToken::String)) + return Error(Parser.getTok().getLoc(), "expected string constant"); + + StringValue = Parser.getTok().getStringContents(); + Parser.Lex(); + } + + if (Parser.parseToken(AsmToken::EndOfStatement, + "unexpected token in '.attribute' directive")) + return true; + + if (Tag == RISCVAttrs::ARCH) { + StringRef Arch = StringValue; + if (Arch.consume_front("rv32")) + clearFeatureBits(RISCV::Feature64Bit, "64bit"); + else if (Arch.consume_front("rv64")) + setFeatureBits(RISCV::Feature64Bit, "64bit"); + else + return Error(ValueExprLoc, "bad arch string " + Arch); + + while (!Arch.empty()) { + if (Arch[0] == 'i') + clearFeatureBits(RISCV::FeatureRV32E, "e"); + else if (Arch[0] == 'e') + setFeatureBits(RISCV::FeatureRV32E, "e"); + else if (Arch[0] == 'g') { + clearFeatureBits(RISCV::FeatureRV32E, "e"); + setFeatureBits(RISCV::FeatureStdExtM, "m"); + setFeatureBits(RISCV::FeatureStdExtA, "a"); + setFeatureBits(RISCV::FeatureStdExtF, "f"); + setFeatureBits(RISCV::FeatureStdExtD, "d"); + } else if (Arch[0] == 'm') + setFeatureBits(RISCV::FeatureStdExtM, "m"); + else if (Arch[0] == 'a') + setFeatureBits(RISCV::FeatureStdExtA, "a"); + else if (Arch[0] == 'f') + setFeatureBits(RISCV::FeatureStdExtF, "f"); + else if (Arch[0] == 'd') { + setFeatureBits(RISCV::FeatureStdExtF, "f"); + setFeatureBits(RISCV::FeatureStdExtD, "d"); + } else if (Arch[0] == 'c') { + setFeatureBits(RISCV::FeatureStdExtC, "c"); + } else + return Error(ValueExprLoc, "bad arch string " + Arch); + + Arch = Arch.drop_front(1); + int major = 0; + int minor = 0; + Arch.consumeInteger(10, major); + Arch.consume_front("p"); + Arch.consumeInteger(10, minor); + if (major != 0 || minor != 0) { + Arch = Arch.drop_until([](char c) { return c == '_' || c == '"'; }); + Arch = Arch.drop_while([](char c) { return c == '_'; }); + } + } + } + + if (IsIntegerValue) + getTargetStreamer().emitAttribute(Tag, IntegerValue); + else { + if (Tag != RISCVAttrs::ARCH) { + getTargetStreamer().emitTextAttribute(Tag, StringValue); + } else { + std::string formalArchStr = "rv32"; + if (getFeatureBits(RISCV::Feature64Bit)) + formalArchStr = "rv64"; + if (getFeatureBits(RISCV::FeatureRV32E)) + formalArchStr = (Twine(formalArchStr) + "e1p9").str(); + else + formalArchStr = (Twine(formalArchStr) + "i2p0").str(); + + if (getFeatureBits(RISCV::FeatureStdExtM)) + formalArchStr = (Twine(formalArchStr) + "_m2p0").str(); + if (getFeatureBits(RISCV::FeatureStdExtA)) + formalArchStr = (Twine(formalArchStr) + "_a2p0").str(); + if (getFeatureBits(RISCV::FeatureStdExtF)) + formalArchStr = (Twine(formalArchStr) + "_f2p0").str(); + if (getFeatureBits(RISCV::FeatureStdExtD)) + formalArchStr = (Twine(formalArchStr) + "_d2p0").str(); + if (getFeatureBits(RISCV::FeatureStdExtC)) + formalArchStr = (Twine(formalArchStr) + "_c2p0").str(); + + getTargetStreamer().emitTextAttribute(Tag, formalArchStr); + } + } + + return false; +} + void RISCVAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) { MCInst CInst; bool Res = compressInst(CInst, Inst, getSTI(), S.getContext()); diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h @@ -15,16 +15,92 @@ namespace llvm { class RISCVTargetELFStreamer : public RISCVTargetStreamer { +private: + enum class AttributeType { Hidden, Numeric, Text, NumericAndText }; + + struct AttributeItem { + AttributeType Type; + unsigned Tag; + unsigned IntValue; + std::string StringValue; + }; + + StringRef CurrentVendor; + SmallVector Contents; + + MCSection *AttributeSection = nullptr; + + AttributeItem *getAttributeItem(unsigned Attribute) { + for (size_t i = 0; i < Contents.size(); ++i) + if (Contents[i].Tag == Attribute) + return &Contents[i]; + return nullptr; + } + + void setAttributeItem(unsigned Attribute, unsigned Value, + bool OverwriteExisting) { + // Look for existing attribute item. + if (AttributeItem *Item = getAttributeItem(Attribute)) { + if (!OverwriteExisting) + return; + Item->Type = AttributeType::Numeric; + Item->IntValue = Value; + return; + } + + // Create new attribute item. + Contents.push_back({AttributeType::Numeric, Attribute, Value, ""}); + } + + void setAttributeItem(unsigned Attribute, StringRef Value, + bool OverwriteExisting) { + // Look for existing attribute item. + if (AttributeItem *Item = getAttributeItem(Attribute)) { + if (!OverwriteExisting) + return; + Item->Type = AttributeType::Text; + Item->StringValue = std::string(Value); + return; + } + + // Create new attribute item. + Contents.push_back({AttributeType::Text, Attribute, 0, std::string(Value)}); + } + + void setAttributeItems(unsigned Attribute, unsigned IntValue, + StringRef StringValue, bool OverwriteExisting) { + // Look for existing attribute item. + if (AttributeItem *Item = getAttributeItem(Attribute)) { + if (!OverwriteExisting) + return; + Item->Type = AttributeType::NumericAndText; + Item->IntValue = IntValue; + Item->StringValue = std::string(StringValue); + return; + } + + // Create new attribute item. + Contents.push_back({AttributeType::NumericAndText, Attribute, IntValue, + std::string(StringValue)}); + } + + void emitAttribute(unsigned Attribute, unsigned Value) override; + void emitTextAttribute(unsigned Attribute, StringRef String) override; + void emitIntTextAttribute(unsigned Attribute, unsigned IntValue, + StringRef StringValue) override; + void finishAttributeSection() override; + size_t calculateContentSize() const; + public: MCELFStreamer &getStreamer(); RISCVTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI); - virtual void emitDirectiveOptionPush(); - virtual void emitDirectiveOptionPop(); - virtual void emitDirectiveOptionRVC(); - virtual void emitDirectiveOptionNoRVC(); - virtual void emitDirectiveOptionRelax(); - virtual void emitDirectiveOptionNoRelax(); + void emitDirectiveOptionPush() override; + void emitDirectiveOptionPop() override; + void emitDirectiveOptionRVC() override; + void emitDirectiveOptionNoRVC() override; + void emitDirectiveOptionRelax() override; + void emitDirectiveOptionNoRelax() override; }; } #endif diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp @@ -15,14 +15,18 @@ #include "RISCVMCTargetDesc.h" #include "Utils/RISCVBaseInfo.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/RISCVAttributes.h" using namespace llvm; // This part is for ELF object output. RISCVTargetELFStreamer::RISCVTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI) - : RISCVTargetStreamer(S) { + : RISCVTargetStreamer(S), CurrentVendor("riscv") { MCAssembler &MCA = getStreamer().getAssembler(); const FeatureBitset &Features = STI.getFeatureBits(); auto &MAB = static_cast(MCA.getBackend()); @@ -66,3 +70,98 @@ void RISCVTargetELFStreamer::emitDirectiveOptionNoRVC() {} void RISCVTargetELFStreamer::emitDirectiveOptionRelax() {} void RISCVTargetELFStreamer::emitDirectiveOptionNoRelax() {} + +void RISCVTargetELFStreamer::emitAttribute(unsigned Attribute, unsigned Value) { + setAttributeItem(Attribute, Value, /*OverwriteExisting=*/true); +} + +void RISCVTargetELFStreamer::emitTextAttribute(unsigned Attribute, + StringRef String) { + setAttributeItem(Attribute, String, /*OverwriteExisting=*/true); +} + +void RISCVTargetELFStreamer::emitIntTextAttribute(unsigned Attribute, + unsigned IntValue, + StringRef StringValue) { + setAttributeItems(Attribute, IntValue, StringValue, + /*OverwriteExisting=*/true); +} + +void RISCVTargetELFStreamer::finishAttributeSection() { + if (Contents.empty()) + return; + + if (AttributeSection) { + Streamer.SwitchSection(AttributeSection); + } else { + MCAssembler &MCA = getStreamer().getAssembler(); + AttributeSection = MCA.getContext().getELFSection( + ".riscv.attributes", ELF::SHT_RISCV_ATTRIBUTES, 0); + Streamer.SwitchSection(AttributeSection); + + Streamer.emitInt8(ELFAttrs::Format_Version); + } + + // Vendor size + Vendor name + '\0' + const size_t VendorHeaderSize = 4 + CurrentVendor.size() + 1; + + // Tag + Tag Size + const size_t TagHeaderSize = 1 + 4; + + const size_t ContentsSize = calculateContentSize(); + + Streamer.emitInt32(VendorHeaderSize + TagHeaderSize + ContentsSize); + Streamer.emitBytes(CurrentVendor); + Streamer.emitInt8(0); // '\0' + + Streamer.emitInt8(ELFAttrs::File); + Streamer.emitInt32(TagHeaderSize + ContentsSize); + + // Size should have been accounted for already, now + // emit each field as its type (ULEB or String). + for (AttributeItem item : Contents) { + Streamer.emitULEB128IntValue(item.Tag); + switch (item.Type) { + default: + llvm_unreachable("Invalid attribute type"); + case AttributeType::Numeric: + Streamer.emitULEB128IntValue(item.IntValue); + break; + case AttributeType::Text: + Streamer.emitBytes(item.StringValue); + Streamer.emitInt8(0); // '\0' + break; + case AttributeType::NumericAndText: + Streamer.emitULEB128IntValue(item.IntValue); + Streamer.emitBytes(item.StringValue); + Streamer.emitInt8(0); // '\0' + break; + } + } + + Contents.clear(); +} + +size_t RISCVTargetELFStreamer::calculateContentSize() const { + size_t Result = 0; + for (AttributeItem item : Contents) { + switch (item.Type) { + case AttributeType::Hidden: + break; + case AttributeType::Numeric: + Result += getULEB128Size(item.Tag); + Result += getULEB128Size(item.IntValue); + break; + case AttributeType::Text: + Result += getULEB128Size(item.Tag); + Result += item.StringValue.size() + 1; // string + '\0' + break; + case AttributeType::NumericAndText: + Result += getULEB128Size(item.Tag); + Result += getULEB128Size(item.IntValue); + Result += item.StringValue.size() + 1; // string + '\0'; + break; + } + } + return Result; +} diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h @@ -10,12 +10,14 @@ #define LLVM_LIB_TARGET_RISCV_RISCVTARGETSTREAMER_H #include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" namespace llvm { class RISCVTargetStreamer : public MCTargetStreamer { public: RISCVTargetStreamer(MCStreamer &S); + void finish() override; virtual void emitDirectiveOptionPush() = 0; virtual void emitDirectiveOptionPop() = 0; @@ -23,12 +25,25 @@ virtual void emitDirectiveOptionNoRVC() = 0; virtual void emitDirectiveOptionRelax() = 0; virtual void emitDirectiveOptionNoRelax() = 0; + virtual void emitAttribute(unsigned Attribute, unsigned Value) = 0; + virtual void finishAttributeSection() = 0; + virtual void emitTextAttribute(unsigned Attribute, StringRef String) = 0; + virtual void emitIntTextAttribute(unsigned Attribute, unsigned IntValue, + StringRef StringValue) = 0; + + void emitTargetAttributes(const MCSubtargetInfo &STI); }; // This part is for ascii assembly output class RISCVTargetAsmStreamer : public RISCVTargetStreamer { formatted_raw_ostream &OS; + void finishAttributeSection() override; + void emitAttribute(unsigned Attribute, unsigned Value) override; + void emitTextAttribute(unsigned Attribute, StringRef String) override; + void emitIntTextAttribute(unsigned Attribute, unsigned IntValue, + StringRef StringValue) override; + public: RISCVTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS); diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp @@ -11,12 +11,43 @@ //===----------------------------------------------------------------------===// #include "RISCVTargetStreamer.h" +#include "RISCVSubtarget.h" #include "llvm/Support/FormattedStream.h" +#include "llvm/Support/RISCVAttributes.h" using namespace llvm; RISCVTargetStreamer::RISCVTargetStreamer(MCStreamer &S) : MCTargetStreamer(S) {} +void RISCVTargetStreamer::finish() { finishAttributeSection(); } + +void RISCVTargetStreamer::emitTargetAttributes(const MCSubtargetInfo &STI) { + if (STI.hasFeature(RISCV::FeatureRV32E)) + emitAttribute(RISCVAttrs::STACK_ALIGN, RISCVAttrs::ALIGN_4); + else + emitAttribute(RISCVAttrs::STACK_ALIGN, RISCVAttrs::ALIGN_16); + + std::string Arch = "rv32"; + if (STI.hasFeature(RISCV::Feature64Bit)) + Arch = "rv64"; + if (STI.hasFeature(RISCV::FeatureRV32E)) + Arch += "e1p9"; + else + Arch += "i2p0"; + if (STI.hasFeature(RISCV::FeatureStdExtM)) + Arch += "_m2p0"; + if (STI.hasFeature(RISCV::FeatureStdExtA)) + Arch += "_a2p0"; + if (STI.hasFeature(RISCV::FeatureStdExtF)) + Arch += "_f2p0"; + if (STI.hasFeature(RISCV::FeatureStdExtD)) + Arch += "_d2p0"; + if (STI.hasFeature(RISCV::FeatureStdExtC)) + Arch += "_c2p0"; + + emitTextAttribute(RISCVAttrs::ARCH, Arch); +} + // This part is for ascii assembly output RISCVTargetAsmStreamer::RISCVTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS) @@ -45,3 +76,18 @@ void RISCVTargetAsmStreamer::emitDirectiveOptionNoRelax() { OS << "\t.option\tnorelax\n"; } + +void RISCVTargetAsmStreamer::emitAttribute(unsigned Attribute, unsigned Value) { + OS << "\t.attribute\t" << Attribute << ", " << Twine(Value) << "\n"; +} + +void RISCVTargetAsmStreamer::emitTextAttribute(unsigned Attribute, + StringRef String) { + OS << "\t.attribute\t" << Attribute << ", \"" << String << "\"\n"; +} + +void RISCVTargetAsmStreamer::emitIntTextAttribute(unsigned Attribute, + unsigned IntValue, + StringRef StringValue) {} + +void RISCVTargetAsmStreamer::finishAttributeSection() {} 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 @@ -11,9 +11,10 @@ // //===----------------------------------------------------------------------===// -#include "RISCV.h" #include "MCTargetDesc/RISCVInstPrinter.h" #include "MCTargetDesc/RISCVMCExpr.h" +#include "MCTargetDesc/RISCVTargetStreamer.h" +#include "RISCV.h" #include "RISCVTargetMachine.h" #include "TargetInfo/RISCVTargetInfo.h" #include "llvm/ADT/Statistic.h" @@ -63,6 +64,12 @@ bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const { return LowerRISCVMachineOperandToMCOperand(MO, MCOp, *this); } + + void emitStartOfAsmFile(Module &M) override; + void emitEndOfAsmFile(Module &M) override; + +private: + void emitAttributes(); }; } @@ -170,6 +177,32 @@ return false; } +void RISCVAsmPrinter::emitStartOfAsmFile(Module &M) { + if (TM.getTargetTriple().isOSBinFormatELF()) + emitAttributes(); +} + +void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) { + RISCVTargetStreamer &RTS = + static_cast(*OutStreamer->getTargetStreamer()); + + if (TM.getTargetTriple().isOSBinFormatELF()) + RTS.finishAttributeSection(); +} + +void RISCVAsmPrinter::emitAttributes() { + RISCVTargetStreamer &RTS = + static_cast(*OutStreamer->getTargetStreamer()); + + const Triple &TT = TM.getTargetTriple(); + StringRef CPU = TM.getTargetCPU(); + StringRef FS = TM.getTargetFeatureString(); + const RISCVTargetMachine &RTM = static_cast(TM); + const RISCVSubtarget STI(TT, CPU, FS, /*ABIName=*/"", RTM); + + RTS.emitTargetAttributes(STI); +} + // Force static initialization. extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVAsmPrinter() { RegisterAsmPrinter X(getTheRISCV32Target()); diff --git a/llvm/test/CodeGen/RISCV/attributes.ll b/llvm/test/CodeGen/RISCV/attributes.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/attributes.ll @@ -0,0 +1,28 @@ +;; Generate ELF attributes from llc. + +; RUN: llc -mtriple=riscv32 -mattr=+m %s -o - | FileCheck --check-prefix=RV32M %s +; RUN: llc -mtriple=riscv32 -mattr=+a %s -o - | FileCheck --check-prefix=RV32A %s +; RUN: llc -mtriple=riscv32 -mattr=+f %s -o - | FileCheck --check-prefix=RV32F %s +; RUN: llc -mtriple=riscv32 -mattr=+d %s -o - | FileCheck --check-prefix=RV32D %s +; RUN: llc -mtriple=riscv32 -mattr=+c %s -o - | FileCheck --check-prefix=RV32C %s +; RUN: llc -mtriple=riscv64 -mattr=+m %s -o - | FileCheck --check-prefix=RV64M %s +; RUN: llc -mtriple=riscv64 -mattr=+a %s -o - | FileCheck --check-prefix=RV64A %s +; RUN: llc -mtriple=riscv64 -mattr=+f %s -o - | FileCheck --check-prefix=RV64F %s +; RUN: llc -mtriple=riscv64 -mattr=+d %s -o - | FileCheck --check-prefix=RV64D %s +; RUN: llc -mtriple=riscv64 -mattr=+c %s -o - | FileCheck --check-prefix=RV64C %s + +; RV32M: .attribute 5, "rv32i2p0_m2p0" +; RV32A: .attribute 5, "rv32i2p0_a2p0" +; RV32F: .attribute 5, "rv32i2p0_f2p0" +; RV32D: .attribute 5, "rv32i2p0_f2p0_d2p0" +; RV32C: .attribute 5, "rv32i2p0_c2p0" +; RV64M: .attribute 5, "rv64i2p0_m2p0" +; RV64A: .attribute 5, "rv64i2p0_a2p0" +; RV64F: .attribute 5, "rv64i2p0_f2p0" +; RV64D: .attribute 5, "rv64i2p0_f2p0_d2p0" +; RV64C: .attribute 5, "rv64i2p0_c2p0" + +define i32 @addi(i32 %a) { + %1 = add i32 %a, 1 + ret i32 %1 +} diff --git a/llvm/test/MC/RISCV/attribute-arch.s b/llvm/test/MC/RISCV/attribute-arch.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/RISCV/attribute-arch.s @@ -0,0 +1,37 @@ +## Arch string without version. + +# RUN: llvm-mc %s -triple=riscv32 -filetype=asm | FileCheck %s +# RUN: llvm-mc %s -triple=riscv64 -filetype=asm | FileCheck %s + +.attribute arch, "rv32i" +# CHECK: attribute 5, "rv32i2p0" + +.attribute arch, "rv32i2" +# CHECK: attribute 5, "rv32i2p0" + +.attribute arch, "rv32i2p" +# CHECK: attribute 5, "rv32i2p0" + +.attribute arch, "rv32i2p0" +# CHECK: attribute 5, "rv32i2p0" + +.attribute arch, "rv32i2_m2" +# CHECK: attribute 5, "rv32i2p0_m2p0" + +.attribute arch, "rv32i2_ma" +# CHECK: attribute 5, "rv32i2p0_m2p0_a2p0" + +.attribute arch, "rv32g" +# CHECK: attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0" + +.attribute arch, "rv32imafdc" +# CHECK: attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0" + +.attribute arch, "rv32i2p0_mafdc" +# CHECK: attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0" + +.attribute arch, "rv32ima2p0_fdc" +# CHECK: attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0" + +.attribute arch, "rv32ima2p_fdc" +# CHECK: attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0" diff --git a/llvm/test/MC/RISCV/attribute-with-insts.s b/llvm/test/MC/RISCV/attribute-with-insts.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/RISCV/attribute-with-insts.s @@ -0,0 +1,34 @@ +## Test .attribute effects. +## We do not provide '-mattr=' and '.option rvc' and enable extensions through +## '.attribute arch'. + +# RUN: llvm-mc -triple riscv32 -filetype=obj %s \ +# RUN: | llvm-objdump --triple=riscv32 -d -M no-aliases - \ +# RUN: | FileCheck -check-prefix=CHECK-INST %s + +# RUN: llvm-mc -triple riscv64 -filetype=obj %s \ +# RUN: | llvm-objdump --triple=riscv64 -d -M no-aliases - \ +# RUN: | FileCheck -check-prefix=CHECK-INST %s + +.attribute arch, "rv64i2p0_m2p0_a2p0_d2p0_c2p0" + +# CHECK-INST: lr.w t0, (t1) +lr.w t0, (t1) + +# CHECK-INST: c.addi a3, -32 +c.addi a3, -32 + +# CHECK-INST: fmadd.d fa0, fa1, fa2, fa3, dyn +fmadd.d f10, f11, f12, f13, dyn + +# CHECK-INST: fmadd.s fa0, fa1, fa2, fa3, dyn +fmadd.s f10, f11, f12, f13, dyn + +# CHECK-INST: addi ra, sp, 2 +addi ra, sp, 2 + +# CHECK-INST: mul a4, ra, s0 +mul a4, ra, s0 + +# CHECK-INST: addw a2, a3, a4 +addw a2, a3, a4 diff --git a/llvm/test/MC/RISCV/attribute.s b/llvm/test/MC/RISCV/attribute.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/RISCV/attribute.s @@ -0,0 +1,22 @@ +## Test llvm-mc could handle .attribute correctly. + +# RUN: llvm-mc %s -triple=riscv32 -filetype=asm | FileCheck %s +# RUN: llvm-mc %s -triple=riscv64 -filetype=asm | FileCheck %s + +.attribute stack_align, 16 +# CHECK: attribute 4, 16 + +.attribute arch, "rv32i2p0_m2p0_a2p0_c2p0" +# CHECK: attribute 5, "rv32i2p0_m2p0_a2p0_c2p0" + +.attribute unaligned_access, 0 +# CHECK: attribute 6, 0 + +.attribute priv_spec, 2 +# CHECK: attribute 8, 2 + +.attribute priv_spec_minor, 0 +# CHECK: attribute 10, 0 + +.attribute priv_spec_revision, 0 +# CHECK: attribute 12, 0 diff --git a/llvm/test/MC/RISCV/invalid-attribute.s b/llvm/test/MC/RISCV/invalid-attribute.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/RISCV/invalid-attribute.s @@ -0,0 +1,31 @@ +## Negative tests: +## - Feed integer value to string type attribute. +## - Feed string value to integer type attribute. +## - Invalid arch string. + +# RUN: not llvm-mc %s -triple=riscv32 -filetype=asm 2>&1 | FileCheck %s +# RUN: not llvm-mc %s -triple=riscv64 -filetype=asm 2>&1 | FileCheck %s + +.attribute arch, "foo" +# CHECK: [[@LINE-1]]:18: error: bad arch string foo + +.attribute arch, "rv32i2p0_y2p0" +# CHECK: [[@LINE-1]]:18: error: bad arch string y2p0 + +.attribute stack_align, "16" +# CHECK: [[@LINE-1]]:25: error: expected numeric constant + +.attribute unaligned_access, "0" +# CHECK: [[@LINE-1]]:30: error: expected numeric constant + +.attribute priv_spec, "2" +# CHECK: [[@LINE-1]]:23: error: expected numeric constant + +.attribute priv_spec_minor, "0" +# CHECK: [[@LINE-1]]:29: error: expected numeric constant + +.attribute priv_spec_revision, "0" +# CHECK: [[@LINE-1]]:32: error: expected numeric constant + +.attribute arch, 30 +# CHECK: [[@LINE-1]]:18: error: expected string constant diff --git a/llvm/test/tools/llvm-objdump/RISCV/lit.local.cfg b/llvm/test/tools/llvm-objdump/RISCV/lit.local.cfg new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/RISCV/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'RISCV' in config.root.targets: + config.unsupported = True diff --git a/llvm/test/tools/llvm-objdump/RISCV/unknown-arch-attr.test b/llvm/test/tools/llvm-objdump/RISCV/unknown-arch-attr.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/RISCV/unknown-arch-attr.test @@ -0,0 +1,38 @@ +## Handle unrecognized arch attributes. +## Encode an unrecognized arch feature into an object file and try to decode it. +## The expected behavior is to ignore the unrecognized arch feature and +## continue to process the following arch features. +## +## The object file has the "rv32i2p0_x1p0_m2p0" arch feature. "x1p0" is an +## unrecognized architecture extension. llvm-objdump will ignore it and decode +## "mul" instruction correctly according to "m2p0" in the arch feature. +## +## This test cannot be assembly because the test needs an unrecognized arch +## feature and `llvm-mc` will filter out the unrecognized arch feature. + +# RUN: yaml2obj %s -D BITS=32 -o %t.32.o +# RUN: llvm-objdump -d %t.32.o \ +# RUN: | FileCheck %s --check-prefixes=DISASM +# RUN: yaml2obj %s -D BITS=64 -o %t.64.o +# RUN: llvm-objdump -d %t.64.o \ +# RUN: | FileCheck %s --check-prefixes=DISASM + +# DISASM: mul a0, a1, a2 + +--- !ELF +FileHeader: + Class: ELFCLASS[[BITS]] + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_RISCV +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] +## The content is the encoding of "mul a0, a1, a2". +## The encoding could be decoded only when the "m" extension is enabled. + Content: 3385C502 + - Name: .riscv.attributes + Type: SHT_RISCV_ATTRIBUTES +## The content is the encoding of the arch feature "rv32i2p0_x1p0_m2p0" + Content: 412300000072697363760001190000000572763332693270305F783170305F6D32703000 diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -2719,7 +2719,7 @@ ArrayRef Contents = unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(&Sec)); - if (Contents[0] != ARMBuildAttrs::Format_Version) { + if (Contents[0] != ELFAttrs::Format_Version) { errs() << "unrecognised FormatVersion: 0x" << Twine::utohexstr(Contents[0]) << '\n'; continue; diff --git a/llvm/unittests/Support/ARMAttributeParser.cpp b/llvm/unittests/Support/ARMAttributeParser.cpp --- a/llvm/unittests/Support/ARMAttributeParser.cpp +++ b/llvm/unittests/Support/ARMAttributeParser.cpp @@ -1,5 +1,6 @@ #include "llvm/Support/ARMAttributeParser.h" #include "llvm/Support/ARMBuildAttributes.h" +#include "llvm/Support/ELFAttributes.h" #include "gtest/gtest.h" #include @@ -36,8 +37,8 @@ ARMAttributeParser Parser; cantFail(Parser.parse(Bytes, support::little)); - return (Parser.hasAttribute(ExpectedTag) && - Parser.getAttributeValue(ExpectedTag) == ExpectedValue); + Optional Attr = Parser.getAttributeValue(ExpectedTag); + return Attr.hasValue() && Attr.getValue() == ExpectedValue; } void testParseError(ArrayRef bytes, const char *msg) { @@ -47,34 +48,8 @@ } bool testTagString(unsigned Tag, const char *name) { - return ARMBuildAttrs::AttrTypeAsString(Tag).str() == name; -} - -TEST(ARMAttributeParser, UnrecognizedFormatVersion) { - static const uint8_t bytes[] = {1}; - testParseError(bytes, "unrecognized format-version: 0x1"); -} - -TEST(ARMAttributeParser, InvalidSubsectionLength) { - static const uint8_t bytes[] = {'A', 3, 0, 0, 0}; - testParseError(bytes, "invalid subsection length 3 at offset 0x1"); -} - -TEST(ARMAttributeParser, UnrecognizedVendorName) { - static const uint8_t bytes[] = {'A', 7, 0, 0, 0, 'x', 'y', 0}; - testParseError(bytes, "unrecognized vendor-name: xy"); -} - -TEST(ARMAttributeParser, InvalidAttributeSize) { - static const uint8_t bytes[] = {'A', 15, 0, 0, 0, 'a', 'e', 'a', - 'b', 'i', 0, 4, 4, 0, 0, 0}; - testParseError(bytes, "invalid attribute size 4 at offset 0xb"); -} - -TEST(ARMAttributeParser, UnrecognizedTag) { - static const uint8_t bytes[] = {'A', 15, 0, 0, 0, 'a', 'e', 'a', - 'b', 'i', 0, 4, 5, 0, 0, 0}; - testParseError(bytes, "unrecognized tag 0x4 at offset 0xb"); + return ELFAttrs::attrTypeAsString(Tag, ARMBuildAttrs::ARMAttributeTags) + .str() == name; } TEST(ARMAttributeParser, UnknownCPU_arch) { diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -28,6 +28,7 @@ DJBTest.cpp EndianStreamTest.cpp EndianTest.cpp + ELFAttributeParserTest.cpp ErrnoTest.cpp ErrorOrTest.cpp ErrorTest.cpp @@ -59,6 +60,7 @@ RegexTest.cpp ReverseIterationTest.cpp ReplaceFileTest.cpp + RISCVAttributeParserTest.cpp ScaledNumberTest.cpp SourceMgrTest.cpp SpecialCaseListTest.cpp diff --git a/llvm/unittests/Support/ELFAttributeParserTest.cpp b/llvm/unittests/Support/ELFAttributeParserTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Support/ELFAttributeParserTest.cpp @@ -0,0 +1,63 @@ +//===----- unittests/ELFAttributeParserTest.cpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/ELFAttributeParser.h" +#include "llvm/Support/ELFAttributes.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +static const TagNameMap emptyTagNameMap; + +// This class is used to test the common part of the ELF attribute section. +class AttributeHeaderParser : public ELFAttributeParser { + Error handler(uint64_t tag, bool &handled) { + // Treat all attributes as handled. + handled = true; + return Error::success(); + } + +public: + AttributeHeaderParser(ScopedPrinter *printer) + : ELFAttributeParser(printer, emptyTagNameMap, "test") {} + AttributeHeaderParser() : ELFAttributeParser(emptyTagNameMap, "test") {} +}; + +static void testParseError(ArrayRef bytes, const char *msg) { + AttributeHeaderParser parser; + Error e = parser.parse(bytes, support::little); + EXPECT_STREQ(toString(std::move(e)).c_str(), msg); +} + +TEST(AttributeHeaderParser, UnrecognizedFormatVersion) { + static const uint8_t bytes[] = {1}; + testParseError(bytes, "unrecognized format-version: 0x1"); +} + +TEST(AttributeHeaderParser, InvalidSubsectionLength) { + static const uint8_t bytes[] = {'A', 3, 0, 0, 0}; + testParseError(bytes, "invalid subsection length 3 at offset 0x1"); +} + +TEST(AttributeHeaderParser, UnrecognizedVendorName) { + static const uint8_t bytes[] = {'A', 7, 0, 0, 0, 'x', 'y', 0}; + testParseError(bytes, "unrecognized vendor-name: xy"); +} + +TEST(AttributeHeaderParser, UnrecognizedTag) { + static const uint8_t bytes[] = {'A', 14, 0, 0, 0, 't', 'e', 's', + 't', 0, 4, 5, 0, 0, 0}; + testParseError(bytes, "unrecognized tag 0x4 at offset 0xa"); +} + +TEST(AttributeHeaderParser, InvalidAttributeSize) { + static const uint8_t bytes[] = {'A', 14, 0, 0, 0, 't', 'e', 's', + 't', 0, 1, 4, 0, 0, 0}; + testParseError(bytes, "invalid attribute size 4 at offset 0xa"); +} diff --git a/llvm/unittests/Support/RISCVAttributeParserTest.cpp b/llvm/unittests/Support/RISCVAttributeParserTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/Support/RISCVAttributeParserTest.cpp @@ -0,0 +1,70 @@ +//===----- unittests/RISCVAttributeParserTest.cpp -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "llvm/Support/RISCVAttributeParser.h" +#include "llvm/Support/ARMBuildAttributes.h" +#include "llvm/Support/ELFAttributes.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +struct RISCVAttributeSection { + unsigned Tag; + unsigned Value; + + RISCVAttributeSection(unsigned tag, unsigned value) + : Tag(tag), Value(value) {} + + void write(raw_ostream &OS) { + OS.flush(); + // length = length + "riscv\0" + TagFile + ByteSize + Tag + Value; + // length = 17 bytes + + OS << 'A' << (uint8_t)17 << (uint8_t)0 << (uint8_t)0 << (uint8_t)0; + OS << "riscv" << '\0'; + OS << (uint8_t)1 << (uint8_t)7 << (uint8_t)0 << (uint8_t)0 << (uint8_t)0; + OS << (uint8_t)Tag << (uint8_t)Value; + } +}; + +static bool testAttribute(unsigned Tag, unsigned Value, unsigned ExpectedTag, + unsigned ExpectedValue) { + std::string buffer; + raw_string_ostream OS(buffer); + RISCVAttributeSection Section(Tag, Value); + Section.write(OS); + ArrayRef Bytes(reinterpret_cast(OS.str().c_str()), + OS.str().size()); + + RISCVAttributeParser Parser; + cantFail(Parser.parse(Bytes, support::little)); + + Optional Attr = Parser.getAttributeValue(ExpectedTag); + return Attr.hasValue() && Attr.getValue() == ExpectedValue; +} + +static bool testTagString(unsigned Tag, const char *name) { + return ELFAttrs::attrTypeAsString(Tag, RISCVAttrs::RISCVAttributeTags) + .str() == name; +} + +TEST(StackAlign, testAttribute) { + EXPECT_TRUE(testTagString(4, "Tag_stack_align")); + EXPECT_TRUE( + testAttribute(4, 4, RISCVAttrs::STACK_ALIGN, RISCVAttrs::ALIGN_4)); + EXPECT_TRUE( + testAttribute(4, 16, RISCVAttrs::STACK_ALIGN, RISCVAttrs::ALIGN_16)); +} + +TEST(UnalignedAccess, testAttribute) { + EXPECT_TRUE(testTagString(6, "Tag_unaligned_access")); + EXPECT_TRUE(testAttribute(6, 0, RISCVAttrs::UNALIGNED_ACCESS, + RISCVAttrs::NOT_ALLOWED)); + EXPECT_TRUE( + testAttribute(6, 1, RISCVAttrs::UNALIGNED_ACCESS, RISCVAttrs::ALLOWED)); +}