[ELF] Merge SHT_RISCV_ATTRIBUTES sections
authorFangrui Song <i@maskray.me>
Thu, 8 Dec 2022 09:53:40 +0000 (09:53 +0000)
committerFangrui Song <i@maskray.me>
Thu, 8 Dec 2022 09:53:40 +0000 (09:53 +0000)
Currently we take the first SHT_RISCV_ATTRIBUTES (.riscv.attributes) as the
output. If we link an object without an extension with an object with the
extension, the output Tag_RISCV_arch may not contain the extension and some
tools like objdump -d will not decode the related instructions.

This patch implements
Tag_RISCV_stack_align/Tag_RISCV_arch/Tag_RISCV_unaligned_access merge as
specified by
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#attributes

For the deprecated Tag_RISCV_priv_spec{,_minor,_revision}, dump the attribute to
the output iff all input agree on the value. This is different from GNU ld but
our simple approach should be ok for deprecated tags.

`RISCVAttributeParser::handler` currently warns about unknown tags. This
behavior is retained. In GNU ld arm, tags >= 64 (mod 128) are ignored with a
warning. If RISC-V ever wants to do something similar
(https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/352), consider
documenting it in the psABI and changing RISCVAttributeParser.

Like GNU ld, zero value integer attributes and empty string attributes are not
dumped to the output.

Reviewed By: asb, kito-cheng

Differential Revision: https://reviews.llvm.org/D138550

lld/ELF/Arch/RISCV.cpp
lld/ELF/Driver.cpp
lld/ELF/InputFiles.cpp
lld/ELF/SyntheticSections.cpp
lld/ELF/SyntheticSections.h
lld/ELF/Target.h
lld/docs/ReleaseNotes.rst
lld/test/ELF/lto/riscv-attributes.ll [new file with mode: 0644]
lld/test/ELF/riscv-attributes-place.s [new file with mode: 0644]
lld/test/ELF/riscv-attributes.s
llvm/include/llvm/Support/RISCVISAInfo.h

index c339f89..a2f5647 100644 (file)
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
+#include "llvm/Support/ELFAttributes.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/RISCVAttributeParser.h"
+#include "llvm/Support/RISCVAttributes.h"
+#include "llvm/Support/RISCVISAInfo.h"
 #include "llvm/Support/TimeProfiler.h"
 
 using namespace llvm;
@@ -816,6 +821,205 @@ void elf::riscvFinalizeRelax(int passes) {
   }
 }
 
+namespace {
+// Representation of the merged .riscv.attributes input sections. The psABI
+// specifies merge policy for attributes. E.g. if we link an object without an
+// extension with an object with the extension, the output Tag_RISCV_arch shall
+// contain the extension. Some tools like objdump parse .riscv.attributes and
+// disabling some instructions if the first Tag_RISCV_arch does not contain an
+// extension.
+class RISCVAttributesSection final : public SyntheticSection {
+public:
+  RISCVAttributesSection()
+      : SyntheticSection(0, SHT_RISCV_ATTRIBUTES, 1, ".riscv.attributes") {}
+
+  size_t getSize() const override { return size; }
+  void writeTo(uint8_t *buf) override;
+
+  static constexpr StringRef vendor = "riscv";
+  DenseMap<unsigned, unsigned> intAttr;
+  DenseMap<unsigned, StringRef> strAttr;
+  size_t size = 0;
+};
+} // namespace
+
+static void mergeArch(RISCVISAInfo::OrderedExtensionMap &mergedExts,
+                      unsigned &mergedXlen, const InputSectionBase *sec,
+                      StringRef s) {
+  auto maybeInfo =
+      RISCVISAInfo::parseArchString(s, /*EnableExperimentalExtension=*/true,
+                                    /*ExperimentalExtensionVersionCheck=*/true);
+  if (!maybeInfo) {
+    errorOrWarn(toString(sec) + ": " + s + ": " +
+                llvm::toString(maybeInfo.takeError()));
+    return;
+  }
+
+  // Merge extensions.
+  RISCVISAInfo &info = **maybeInfo;
+  if (mergedExts.empty()) {
+    mergedExts = info.getExtensions();
+    mergedXlen = info.getXLen();
+  } else {
+    for (const auto &ext : info.getExtensions()) {
+      if (auto it = mergedExts.find(ext.first); it != mergedExts.end()) {
+        // TODO This is untested because RISCVISAInfo::parseArchString does not
+        // accept unsupported versions yet.
+        if (std::tie(it->second.MajorVersion, it->second.MinorVersion) >=
+            std::tie(ext.second.MajorVersion, ext.second.MinorVersion))
+          continue;
+      }
+      mergedExts[ext.first] = ext.second;
+    }
+  }
+}
+
+static RISCVAttributesSection *
+mergeAttributesSection(const SmallVector<InputSectionBase *, 0> &sections) {
+  RISCVISAInfo::OrderedExtensionMap exts;
+  const InputSectionBase *firstStackAlign = nullptr;
+  unsigned firstStackAlignValue = 0, xlen = 0;
+  bool hasArch = false;
+
+  in.riscvAttributes = std::make_unique<RISCVAttributesSection>();
+  auto &merged = static_cast<RISCVAttributesSection &>(*in.riscvAttributes);
+
+  // Collect all tags values from attributes section.
+  const auto &attributesTags = RISCVAttrs::getRISCVAttributeTags();
+  for (const InputSectionBase *sec : sections) {
+    RISCVAttributeParser parser;
+    if (Error e = parser.parse(sec->content(), support::little))
+      warn(toString(sec) + ": " + llvm::toString(std::move(e)));
+    for (const auto &tag : attributesTags) {
+      switch (RISCVAttrs::AttrType(tag.attr)) {
+        // Integer attributes.
+      case RISCVAttrs::STACK_ALIGN:
+        if (auto i = parser.getAttributeValue(tag.attr)) {
+          auto r = merged.intAttr.try_emplace(tag.attr, *i);
+          if (r.second) {
+            firstStackAlign = sec;
+            firstStackAlignValue = *i;
+          } else if (r.first->second != *i) {
+            errorOrWarn(toString(sec) + " has stack_align=" + Twine(*i) +
+                        " but " + toString(firstStackAlign) +
+                        " has stack_align=" + Twine(firstStackAlignValue));
+          }
+        }
+        continue;
+      case RISCVAttrs::UNALIGNED_ACCESS:
+        if (auto i = parser.getAttributeValue(tag.attr))
+          merged.intAttr[tag.attr] |= *i;
+        continue;
+
+        // String attributes.
+      case RISCVAttrs::ARCH:
+        if (auto s = parser.getAttributeString(tag.attr)) {
+          hasArch = true;
+          mergeArch(exts, xlen, sec, *s);
+        }
+        continue;
+
+        // Attributes which use the default handling.
+      case RISCVAttrs::PRIV_SPEC:
+      case RISCVAttrs::PRIV_SPEC_MINOR:
+      case RISCVAttrs::PRIV_SPEC_REVISION:
+        break;
+      }
+
+      // Fallback for deprecated priv_spec* and other unknown attributes: retain
+      // the attribute if all input sections agree on the value. GNU ld uses 0
+      // and empty strings as default values which are not dumped to the output.
+      // TODO Adjust after resolution to
+      // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/352
+      if (tag.attr % 2 == 0) {
+        if (auto i = parser.getAttributeValue(tag.attr)) {
+          auto r = merged.intAttr.try_emplace(tag.attr, *i);
+          if (!r.second && r.first->second != *i)
+            r.first->second = 0;
+        }
+      } else if (auto s = parser.getAttributeString(tag.attr)) {
+        auto r = merged.strAttr.try_emplace(tag.attr, *s);
+        if (!r.second && r.first->second != *s)
+          r.first->second = {};
+      }
+    }
+  }
+
+  if (hasArch) {
+    if (auto result = RISCVISAInfo::postProcessAndChecking(
+            std::make_unique<RISCVISAInfo>(xlen, exts))) {
+      merged.strAttr.try_emplace(RISCVAttrs::ARCH,
+                                 saver().save((*result)->toString()));
+    } else {
+      errorOrWarn(llvm::toString(result.takeError()));
+    }
+  }
+
+  // The total size of headers: format-version [ <section-length> "vendor-name"
+  // [ <file-tag> <size>.
+  size_t size = 5 + merged.vendor.size() + 1 + 5;
+  for (auto &attr : merged.intAttr)
+    if (attr.second != 0)
+      size += getULEB128Size(attr.first) + getULEB128Size(attr.second);
+  for (auto &attr : merged.strAttr)
+    if (!attr.second.empty())
+      size += getULEB128Size(attr.first) + attr.second.size() + 1;
+  merged.size = size;
+  return &merged;
+}
+
+void RISCVAttributesSection::writeTo(uint8_t *buf) {
+  const size_t size = getSize();
+  uint8_t *const end = buf + size;
+  *buf = ELFAttrs::Format_Version;
+  write32(buf + 1, size - 1);
+  buf += 5;
+
+  memcpy(buf, vendor.data(), vendor.size());
+  buf += vendor.size() + 1;
+
+  *buf = ELFAttrs::File;
+  write32(buf + 1, end - buf);
+  buf += 5;
+
+  for (auto &attr : intAttr) {
+    if (attr.second == 0)
+      continue;
+    buf += encodeULEB128(attr.first, buf);
+    buf += encodeULEB128(attr.second, buf);
+  }
+  for (auto &attr : strAttr) {
+    if (attr.second.empty())
+      continue;
+    buf += encodeULEB128(attr.first, buf);
+    memcpy(buf, attr.second.data(), attr.second.size());
+    buf += attr.second.size() + 1;
+  }
+}
+
+void elf::mergeRISCVAttributesSections() {
+  // Find the first input SHT_RISCV_ATTRIBUTES; return if not found.
+  size_t place =
+      llvm::find_if(ctx.inputSections,
+                    [](auto *s) { return s->type == SHT_RISCV_ATTRIBUTES; }) -
+      ctx.inputSections.begin();
+  if (place == ctx.inputSections.size())
+    return;
+
+  // Extract all SHT_RISCV_ATTRIBUTES sections into `sections`.
+  SmallVector<InputSectionBase *, 0> sections;
+  llvm::erase_if(ctx.inputSections, [&](InputSectionBase *s) {
+    if (s->type != SHT_RISCV_ATTRIBUTES)
+      return false;
+    sections.push_back(s);
+    return true;
+  });
+
+  // Add the merged section.
+  ctx.inputSections.insert(ctx.inputSections.begin() + place,
+                           mergeAttributesSection(sections));
+}
+
 TargetInfo *elf::getRISCVTargetInfo() {
   static RISCV target;
   return &target;
index a7d1cab..a051228 100644 (file)
@@ -2820,6 +2820,10 @@ void LinkerDriver::link(opt::InputArgList &args) {
   if (!config->relocatable)
     combineEhSections();
 
+  // Merge .riscv.attributes sections.
+  if (config->emachine == EM_RISCV)
+    mergeRISCVAttributesSections();
+
   {
     llvm::TimeTraceScope timeScope("Assign sections");
 
index 7edb696..aafef65 100644 (file)
@@ -581,30 +581,6 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
       }
     }
 
-    if (sec.sh_type == SHT_RISCV_ATTRIBUTES && config->emachine == EM_RISCV) {
-      RISCVAttributeParser attributes;
-      ArrayRef<uint8_t> contents =
-          check(this->getObj().getSectionContents(sec));
-      StringRef name = check(obj.getSectionName(sec, shstrtab));
-      this->sections[i] = &InputSection::discarded;
-      if (Error e = attributes.parse(contents, support::little)) {
-        InputSection isec(*this, sec, name);
-        warn(toString(&isec) + ": " + llvm::toString(std::move(e)));
-      } else {
-        // FIXME: Validate arch tag contains C if and only if EF_RISCV_RVC is
-        // present.
-
-        // FIXME: Retain the first attribute section we see. Tools such as
-        // llvm-objdump make use of the attribute section to determine which
-        // standard extensions to enable. In a full implementation we would
-        // merge all attribute sections.
-        if (in.attributes == nullptr) {
-          in.attributes = std::make_unique<InputSection>(*this, sec, name);
-          this->sections[i] = in.attributes.get();
-        }
-      }
-    }
-
     if (sec.sh_type != SHT_GROUP)
       continue;
     StringRef signature = getShtGroupSignature(objSections, sec);
index 2ca3b63..b7e5114 100644 (file)
@@ -3807,6 +3807,7 @@ void PartitionIndexSection::writeTo(uint8_t *buf) {
 
 void InStruct::reset() {
   attributes.reset();
+  riscvAttributes.reset();
   bss.reset();
   bssRelRo.reset();
   got.reset();
index 3dc9c8e..391eb6b 100644 (file)
@@ -1273,6 +1273,7 @@ inline Partition &SectionBase::getPartition() const {
 // a partition.
 struct InStruct {
   std::unique_ptr<InputSection> attributes;
+  std::unique_ptr<SyntheticSection> riscvAttributes;
   std::unique_ptr<BssSection> bss;
   std::unique_ptr<BssSection> bssRelRo;
   std::unique_ptr<GotSection> got;
index fb3d8ff..e6a7816 100644 (file)
@@ -216,6 +216,7 @@ void addPPC64SaveRestore();
 uint64_t getPPC64TocBase();
 uint64_t getAArch64Page(uint64_t expr);
 void riscvFinalizeRelax(int passes);
+void mergeRISCVAttributesSections();
 
 LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target;
 TargetInfo *getTarget();
index cf52fd3..c350aa9 100644 (file)
@@ -39,6 +39,9 @@ ELF Improvements
 * ``--no-undefined-version`` is now the default; symbols named in version
   scripts that have no matching symbol in the output will be reported. Use
   ``--undefined-version`` to revert to the old behavior.
+* The output ``SHT_RISCV_ATTRIBUTES`` section now merges all input components
+  instead of picking the first input component.
+  (`D138550 <https://reviews.llvm.org/D138550>`_)
 
 Breaking changes
 ----------------
diff --git a/lld/test/ELF/lto/riscv-attributes.ll b/lld/test/ELF/lto/riscv-attributes.ll
new file mode 100644 (file)
index 0000000..eb99397
--- /dev/null
@@ -0,0 +1,53 @@
+; REQUIRES: riscv
+
+; RUN: rm -rf %t && split-file %s %t && cd %t
+; RUN: llvm-mc -filetype=obj -triple=riscv32 1.s -o 1.o
+; RUN: llvm-mc -filetype=obj -triple=riscv32 2.s -o 2.o
+; RUN: llvm-as a.ll -o a.bc
+; RUN: ld.lld 1.o 2.o a.bc -o out
+; RUN: llvm-readelf --arch-specific out | FileCheck %s
+
+; CHECK:      BuildAttributes {
+; CHECK-NEXT:   FormatVersion: 0x41
+; CHECK-NEXT:   Section 1 {
+; CHECK-NEXT:     SectionLength: 61
+; CHECK-NEXT:     Vendor: riscv
+; CHECK-NEXT:     Tag: Tag_File (0x1)
+; CHECK-NEXT:     Size: 51
+; CHECK-NEXT:     FileAttributes {
+; CHECK-NEXT:       Attribute {
+; CHECK-NEXT:         Tag: 4
+; CHECK-NEXT:         Value: 16
+; CHECK-NEXT:         TagName: stack_align
+; CHECK-NEXT:         Description: Stack alignment is 16-bytes
+; CHECK-NEXT:       }
+; CHECK-NEXT:       Attribute {
+; CHECK-NEXT:         Tag: 6
+; CHECK-NEXT:         Value: 1
+; CHECK-NEXT:         TagName: unaligned_access
+; CHECK-NEXT:         Description: Unaligned access
+; CHECK-NEXT:       }
+; CHECK-NEXT:       Attribute {
+; CHECK-NEXT:         Tag: 5
+; CHECK-NEXT:         TagName: arch
+; CHECK-NEXT:         Value: rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0_zbb1p0
+; CHECK-NEXT:       }
+; CHECK-NEXT:     }
+; CHECK-NEXT:   }
+; CHECK-NEXT: }
+
+;--- 1.s
+.attribute 4, 16
+.attribute 5, "rv32i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
+;--- 2.s
+.attribute 4, 16
+.attribute 5, "rv32i2p0_m2p0_f2p0_d2p0_zbb1p0"
+.attribute 6, 1
+
+;--- a.ll
+target datalayout = "e-m:e-p:32:32-i64:64-n32-S128"
+target triple = "riscv32"
+
+define void @_start() {
+  ret void
+}
diff --git a/lld/test/ELF/riscv-attributes-place.s b/lld/test/ELF/riscv-attributes-place.s
new file mode 100644 (file)
index 0000000..4d265bd
--- /dev/null
@@ -0,0 +1,29 @@
+# REQUIRES: riscv
+## The merged SHT_RISCV_ATTRIBUTES is placed at the first input
+## SHT_RISCV_ATTRIBUTES.
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 %s -o %t.o
+# RUN: ld.lld -e 0 %t.o %t.o -o %t
+# RUN: llvm-readelf -S %t | FileCheck %s
+
+# CHECK:      Name              Type             Address          Off      Size   ES Flg Lk Inf Al
+# CHECK:      .riscv.a          PROGBITS         0000000000000000 [[#%x,]] 000002 00      0   0  1
+# CHECK-NEXT: .riscv.attributes RISCV_ATTRIBUTES 0000000000000000 [[#%x,]] 00001a 00      0   0  1
+# CHECK-NEXT: .riscv.b          PROGBITS         0000000000000000 [[#%x,]] 000002 00      0   0  1
+
+.section .riscv.a,""
+.byte 0
+
+.section .riscv.attributes,"",@0x70000003
+.byte 0x41
+.long .Lend-.riscv.attributes-1
+.asciz "riscv"  # vendor
+.Lbegin:
+.byte 1  # Tag_File
+.long .Lend-.Lbegin
+.byte 5  # Tag_RISCV_arch
+.asciz "rv64i2"
+.Lend:
+
+.section .riscv.b,""
+.byte 0
index 36e506a..3776673 100644 (file)
@@ -1,10 +1,51 @@
 # REQUIRES: riscv
 
-# RUN: llvm-mc -filetype=obj -triple=riscv64-unknown-elf -mattr=-relax %s -o %t.o
-# RUN: ld.lld %t.o -o %t
-# RUN: llvm-readelf --arch-specific %t | FileCheck %s
-# RUN: ld.lld %t.o %t.o -o %t2
-# RUN: llvm-readelf --arch-specific %t2 | FileCheck %s
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=riscv64 a.s -o a.o
+# RUN: ld.lld -e 0 a.o -o out 2>&1 | count 0
+# RUN: llvm-readobj --arch-specific out | FileCheck %s
+# RUN: ld.lld -e 0 a.o a.o -o out1 2>&1 | count 0
+# RUN: llvm-readobj --arch-specific out1 | FileCheck %s
+# RUN: ld.lld -r a.o a.o -o out1 2>&1 | count 0
+# RUN: llvm-readobj --arch-specific out1 | FileCheck %s
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 b.s -o b.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 c.s -o c.o
+# RUN: ld.lld a.o b.o c.o -o out2
+# RUN: llvm-readobj --arch-specific out2 | FileCheck %s --check-prefix=CHECK2
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 invalid_ext.s -o invalid_ext.o
+# RUN: not ld.lld invalid_ext.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=INVALID_EXT --implicit-check-not=error:
+# INVALID_EXT: error: invalid_ext.o:(.riscv.attributes): rv64i2p0_y2p0: invalid standard user-level extension 'y'
+
+## A zero value attribute is not printed.
+# RUN: llvm-mc -filetype=obj -triple=riscv64 unaligned_access_0.s -o unaligned_access_0.o
+# RUN: ld.lld -e 0 --fatal-warnings a.o unaligned_access_0.o -o unaligned_access_0
+# RUN: llvm-readobj -A unaligned_access_0 | FileCheck /dev/null --implicit-check-not='TagName: unaligned_access'
+
+## Differing stack_align values lead to an error.
+# RUN: llvm-mc -filetype=obj -triple=riscv64 diff_stack_align.s -o diff_stack_align.o
+# RUN: not ld.lld a.o b.o c.o diff_stack_align.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=STACK_ALIGN --implicit-check-not=error:
+# STACK_ALIGN: error: diff_stack_align.o:(.riscv.attributes) has stack_align=32 but a.o:(.riscv.attributes) has stack_align=16
+
+## The deprecated priv_spec is not handled as GNU ld does.
+## Differing priv_spec attributes lead to an absent attribute.
+# RUN: llvm-mc -filetype=obj -triple=riscv64 diff_priv_spec.s -o diff_priv_spec.o
+# RUN: ld.lld -e 0 --fatal-warnings a.o b.o c.o diff_priv_spec.o -o diff_priv_spec
+# RUN: llvm-readobj -A diff_priv_spec | FileCheck /dev/null --implicit-check-not='TagName: priv_spec'
+
+## Unknown tags currently lead to warnings.
+# RUN: llvm-mc -filetype=obj -triple=riscv64 unknown13.s -o unknown13.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 unknown13a.s -o unknown13a.o
+# RUN: ld.lld -e 0 unknown13.o unknown13.o unknown13a.o -o unknown13 2>&1 | FileCheck %s --check-prefix=UNKNOWN13 --implicit-check-not=warning:
+# UNKNOWN13-COUNT-2: warning: unknown13.o:(.riscv.attributes): invalid tag 0xd at offset 0x10
+# UNKNOWN13:         warning: unknown13a.o:(.riscv.attributes): invalid tag 0xd at offset 0x10
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 unknown22.s -o unknown22.o
+# RUN: llvm-mc -filetype=obj -triple=riscv64 unknown22a.s -o unknown22a.o
+# RUN: ld.lld -e 0 unknown22.o unknown22.o unknown22a.o -o unknown22 2>&1 | FileCheck %s --check-prefix=UNKNOWN22 --implicit-check-not=warning:
+# UNKNOWN22-COUNT-2: warning: unknown22.o:(.riscv.attributes): invalid tag 0x16 at offset 0x10
+# UNKNOWN22:         warning: unknown22a.o:(.riscv.attributes): invalid tag 0x16 at offset 0x10
 
 # CHECK:      BuildAttributes {
 # CHECK-NEXT:   FormatVersion: 0x41
 # CHECK-NEXT:   }
 # CHECK-NEXT: }
 
-.attribute 4, 16
-.attribute 5, "rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
+# CHECK2:      BuildAttributes {
+# CHECK2-NEXT:   FormatVersion: 0x41
+# CHECK2-NEXT:   Section 1 {
+# CHECK2-NEXT:     SectionLength: 95
+# CHECK2-NEXT:     Vendor: riscv
+# CHECK2-NEXT:     Tag: Tag_File (0x1)
+# CHECK2-NEXT:     Size: 85
+# CHECK2-NEXT:     FileAttributes {
+# CHECK2-NEXT:       Attribute {
+# CHECK2-NEXT:         Tag: 4
+# CHECK2-NEXT:         Value: 16
+# CHECK2-NEXT:         TagName: stack_align
+# CHECK2-NEXT:         Description: Stack alignment is 16-bytes
+# CHECK2-NEXT:       }
+# CHECK2-NEXT:       Attribute {
+# CHECK2-NEXT:         Tag: 6
+# CHECK2-NEXT:         Value: 1
+# CHECK2-NEXT:         TagName: unaligned_access
+# CHECK2-NEXT:         Description: Unaligned access
+# CHECK2-NEXT:       }
+# CHECK2-NEXT:       Attribute {
+# CHECK2-NEXT:         Tag: 8
+# CHECK2-NEXT:         TagName: priv_spec
+# CHECK2-NEXT:         Value: 2
+# CHECK2-NEXT:       }
+# CHECK2-NEXT:       Attribute {
+# CHECK2-NEXT:         Tag: 10
+# CHECK2-NEXT:         TagName: priv_spec_minor
+# CHECK2-NEXT:         Value: 2
+# CHECK2-NEXT:       }
+# CHECK2-NEXT:       Attribute {
+# CHECK2-NEXT:         Tag: 5
+# CHECK2-NEXT:         TagName: arch
+# CHECK2-NEXT:         Value: rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0_zkt1p0_zve32f1p0_zve32x1p0_zvl32b1p0
+# CHECK2-NEXT:       }
+# CHECK2-NEXT:     }
+# CHECK2-NEXT:   }
+# CHECK2-NEXT: }
+
+#--- a.s
+.attribute stack_align, 16
+.attribute arch, "rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
+.attribute unaligned_access, 0
+
+#--- b.s
+.attribute stack_align, 16
+.attribute arch, "rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
+.attribute priv_spec, 2
+.attribute priv_spec_minor, 2
+
+#--- c.s
+.attribute stack_align, 16
+.attribute arch, "rv64i2p0_f2p0_zkt1p0_zve32f1p0_zve32x1p0_zvl32b1p0"
+.attribute unaligned_access, 1
+.attribute priv_spec, 2
+.attribute priv_spec_minor, 2
+
+#--- invalid_ext.s
+.section .riscv.attributes,"",@0x70000003
+.byte 0x41
+.long .Lend-.riscv.attributes-1
+.asciz "riscv"  # vendor
+.Lbegin:
+.byte 1  # Tag_File
+.long .Lend-.Lbegin
+.byte 5  # Tag_RISCV_arch
+.asciz "rv64i2p0_y2p0"
+.Lend:
+
+#--- unaligned_access_0.s
+.attribute unaligned_access, 0
+
+#--- diff_stack_align.s
+.attribute stack_align, 32
+
+#--- diff_priv_spec.s
+.attribute priv_spec, 3
+.attribute priv_spec_minor, 3
+
+#--- unknown13.s
+.attribute 13, "0"
+#--- unknown13a.s
+.attribute 13, "1"
+
+#--- unknown22.s
+.attribute 22, 1
+#--- unknown22a.s
+.attribute 22, 2
index ced3baf..4529b74 100644 (file)
@@ -42,6 +42,9 @@ public:
   typedef std::map<std::string, RISCVExtensionInfo, ExtensionComparator>
       OrderedExtensionMap;
 
+  RISCVISAInfo(unsigned XLen, OrderedExtensionMap &Exts)
+      : XLen(XLen), FLen(0), MinVLen(0), MaxELen(0), MaxELenFp(0), Exts(Exts) {}
+
   /// Parse RISCV ISA info from arch string.
   static llvm::Expected<std::unique_ptr<RISCVISAInfo>>
   parseArchString(StringRef Arch, bool EnableExperimentalExtension,
@@ -73,6 +76,8 @@ public:
   static bool isSupportedExtension(StringRef Ext);
   static bool isSupportedExtension(StringRef Ext, unsigned MajorVersion,
                                    unsigned MinorVersion);
+  static llvm::Expected<std::unique_ptr<RISCVISAInfo>>
+  postProcessAndChecking(std::unique_ptr<RISCVISAInfo> &&ISAInfo);
 
 private:
   RISCVISAInfo(unsigned XLen)
@@ -95,9 +100,6 @@ private:
   void updateFLen();
   void updateMinVLen();
   void updateMaxELen();
-
-  static llvm::Expected<std::unique_ptr<RISCVISAInfo>>
-  postProcessAndChecking(std::unique_ptr<RISCVISAInfo> &&ISAInfo);
 };
 
 } // namespace llvm