Index: lld/ELF/Arch/RISCV.cpp =================================================================== --- lld/ELF/Arch/RISCV.cpp +++ lld/ELF/Arch/RISCV.cpp @@ -846,9 +846,7 @@ static void mergeArch(RISCVISAInfo::OrderedExtensionMap &mergedExts, unsigned &mergedXlen, const InputSectionBase *sec, StringRef s) { - auto maybeInfo = - RISCVISAInfo::parseArchString(s, /*EnableExperimentalExtension=*/true, - /*ExperimentalExtensionVersionCheck=*/true); + auto maybeInfo = RISCVISAInfo::parseNormalizedArchString(s); if (!maybeInfo) { errorOrWarn(toString(sec) + ": " + s + ": " + llvm::toString(maybeInfo.takeError())); @@ -863,8 +861,6 @@ } 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; Index: lld/test/ELF/riscv-attributes.s =================================================================== --- lld/test/ELF/riscv-attributes.s +++ lld/test/ELF/riscv-attributes.s @@ -15,20 +15,24 @@ # RUN: llvm-readobj --arch-specific out2 | FileCheck %s --check-prefix=CHECK2 # RUN: llvm-mc -filetype=obj -triple=riscv64 unrecognized_ext1.s -o unrecognized_ext1.o -# RUN: not ld.lld unrecognized_ext1.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNRECOGNIZED_EXT1 --implicit-check-not=error: -# UNRECOGNIZED_EXT1: error: unrecognized_ext1.o:(.riscv.attributes): rv64i2p0_y2p0: invalid standard user-level extension 'y' +# RUN: ld.lld -e 0 unrecognized_ext1.o -o unrecognized_ext1 +# RUN: llvm-readobj --arch-specific unrecognized_ext1 | FileCheck %s --check-prefix=UNRECOGNIZED_EXT1 # RUN: llvm-mc -filetype=obj -triple=riscv64 unrecognized_ext2.s -o unrecognized_ext2.o -# RUN: not ld.lld unrecognized_ext2.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNRECOGNIZED_EXT2 --implicit-check-not=error: -# UNRECOGNIZED_EXT2: error: unrecognized_ext2.o:(.riscv.attributes): rv64i2p0_zmadeup1p0: unsupported version number 1.0 for extension 'zmadeup' +# RUN: ld.lld -e 0 unrecognized_ext2.o -o unrecognized_ext2 +# RUN: llvm-readobj --arch-specific unrecognized_ext2 | FileCheck %s --check-prefix=UNRECOGNIZED_EXT2 # RUN: llvm-mc -filetype=obj -triple=riscv64 unrecognized_version.s -o unrecognized_version.o -# RUN: not ld.lld unrecognized_version.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNRECOGNIZED_VERSION --implicit-check-not=error: -# UNRECOGNIZED_VERSION: error: unrecognized_version.o:(.riscv.attributes): rv64i99p0: unsupported version number 99.0 for extension 'i' +# RUN: ld.lld -e 0 unrecognized_version.o -o unrecognized_version +# RUN: llvm-readobj --arch-specific unrecognized_version | FileCheck %s --check-prefix=UNRECOGNIZED_VERSION + +# RUN: llvm-mc -filetype=obj -triple=riscv64 merge_version_test_input.s -o merge_version_test_input.o +# RUN: ld.lld unrecognized_version.o merge_version_test_input.o -o out3 +# RUN: llvm-readobj --arch-specific out3 | FileCheck %s --check-prefix=CHECK3 # RUN: llvm-mc -filetype=obj -triple=riscv64 invalid_arch1.s -o invalid_arch1.o -# RUN: ld.lld -e 0 invalid_arch1.o -o invalid_arch1 -# RUN: llvm-readobj --arch-specific invalid_arch1 | FileCheck %s --check-prefix=INVALID_ARCH1 +# RUN: not ld.lld invalid_arch1.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=INVALID_ARCH1 --implicit-check-not=error: +# INVALID_ARCH1: error: invalid_arch1.o:(.riscv.attributes): rv64i2: extension lacks version in expected format ## A zero value attribute is not printed. # RUN: llvm-mc -filetype=obj -triple=riscv64 unaligned_access_0.s -o unaligned_access_0.o @@ -121,6 +125,23 @@ # CHECK2-NEXT: } # CHECK2-NEXT: } +# CHECK3: BuildAttributes { +# CHECK3-NEXT: FormatVersion: 0x41 +# CHECK3-NEXT: Section 1 { +# CHECK3-NEXT: SectionLength: 26 +# CHECK3-NEXT: Vendor: riscv +# CHECK3-NEXT: Tag: Tag_File (0x1) +# CHECK3-NEXT: Size: 16 +# CHECK3-NEXT: FileAttributes { +# CHECK3-NEXT: Attribute { +# CHECK3-NEXT: Tag: 5 +# CHECK3-NEXT: TagName: arch +# CHECK3-NEXT: Value: rv64i99p0 +# CHECK3-NEXT: } +# CHECK3-NEXT: } +# CHECK3-NEXT: } +# CHECK3-NEXT: } + #--- a.s .attribute stack_align, 16 .attribute arch, "rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0" @@ -140,6 +161,22 @@ .attribute priv_spec_minor, 2 #--- unrecognized_ext1.s +# UNRECOGNIZED_EXT1: BuildAttributes { +# UNRECOGNIZED_EXT1-NEXT: FormatVersion: 0x41 +# UNRECOGNIZED_EXT1-NEXT: Section 1 { +# UNRECOGNIZED_EXT1-NEXT: SectionLength: 30 +# UNRECOGNIZED_EXT1-NEXT: Vendor: riscv +# UNRECOGNIZED_EXT1-NEXT: Tag: Tag_File (0x1) +# UNRECOGNIZED_EXT1-NEXT: Size: 20 +# UNRECOGNIZED_EXT1-NEXT: FileAttributes { +# UNRECOGNIZED_EXT1-NEXT: Attribute { +# UNRECOGNIZED_EXT1-NEXT: Tag: 5 +# UNRECOGNIZED_EXT1-NEXT: TagName: arch +# UNRECOGNIZED_EXT1-NEXT: Value: rv64i2p0_y2p0 +# UNRECOGNIZED_EXT1-NEXT: } +# UNRECOGNIZED_EXT1-NEXT: } +# UNRECOGNIZED_EXT1-NEXT: } +# UNRECOGNIZED_EXT1-NEXT: } .section .riscv.attributes,"",@0x70000003 .byte 0x41 .long .Lend-.riscv.attributes-1 @@ -152,6 +189,22 @@ .Lend: #--- unrecognized_ext2.s +# UNRECOGNIZED_EXT2: BuildAttributes { +# UNRECOGNIZED_EXT2-NEXT: FormatVersion: 0x41 +# UNRECOGNIZED_EXT2-NEXT: Section 1 { +# UNRECOGNIZED_EXT2-NEXT: SectionLength: 36 +# UNRECOGNIZED_EXT2-NEXT: Vendor: riscv +# UNRECOGNIZED_EXT2-NEXT: Tag: Tag_File (0x1) +# UNRECOGNIZED_EXT2-NEXT: Size: 26 +# UNRECOGNIZED_EXT2-NEXT: FileAttributes { +# UNRECOGNIZED_EXT2-NEXT: Attribute { +# UNRECOGNIZED_EXT2-NEXT: Tag: 5 +# UNRECOGNIZED_EXT2-NEXT: TagName: arch +# UNRECOGNIZED_EXT2-NEXT: Value: rv64i2p0_zmadeup1p0 +# UNRECOGNIZED_EXT2-NEXT: } +# UNRECOGNIZED_EXT2-NEXT: } +# UNRECOGNIZED_EXT2-NEXT: } +# UNRECOGNIZED_EXT2-NEXT: } .section .riscv.attributes,"",@0x70000003 .byte 0x41 .long .Lend-.riscv.attributes-1 @@ -164,6 +217,22 @@ .Lend: #--- unrecognized_version.s +# UNRECOGNIZED_VERSION: BuildAttributes { +# UNRECOGNIZED_VERSION-NEXT: FormatVersion: 0x41 +# UNRECOGNIZED_VERSION-NEXT: Section 1 { +# UNRECOGNIZED_VERSION-NEXT: SectionLength: 26 +# UNRECOGNIZED_VERSION-NEXT: Vendor: riscv +# UNRECOGNIZED_VERSION-NEXT: Tag: Tag_File (0x1) +# UNRECOGNIZED_VERSION-NEXT: Size: 16 +# UNRECOGNIZED_VERSION-NEXT: FileAttributes { +# UNRECOGNIZED_VERSION-NEXT: Attribute { +# UNRECOGNIZED_VERSION-NEXT: Tag: 5 +# UNRECOGNIZED_VERSION-NEXT: TagName: arch +# UNRECOGNIZED_VERSION-NEXT: Value: rv64i99p0 +# UNRECOGNIZED_VERSION-NEXT: } +# UNRECOGNIZED_VERSION-NEXT: } +# UNRECOGNIZED_VERSION-NEXT: } +# UNRECOGNIZED_VERSION-NEXT: } .section .riscv.attributes,"",@0x70000003 .byte 0x41 .long .Lend-.riscv.attributes-1 @@ -175,23 +244,19 @@ .asciz "rv64i99p0" .Lend: +#--- merge_version_test_input.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 "rv64i2p1" +.Lend: + #--- invalid_arch1.s -# INVALID_ARCH1: BuildAttributes { -# INVALID_ARCH1-NEXT: FormatVersion: 0x41 -# INVALID_ARCH1-NEXT: Section 1 { -# INVALID_ARCH1-NEXT: SectionLength: 25 -# INVALID_ARCH1-NEXT: Vendor: riscv -# INVALID_ARCH1-NEXT: Tag: Tag_File (0x1) -# INVALID_ARCH1-NEXT: Size: 15 -# INVALID_ARCH1-NEXT: FileAttributes { -# INVALID_ARCH1-NEXT: Attribute { -# INVALID_ARCH1-NEXT: Tag: 5 -# INVALID_ARCH1-NEXT: TagName: arch -# INVALID_ARCH1-NEXT: Value: rv64i2p0 -# INVALID_ARCH1-NEXT: } -# INVALID_ARCH1-NEXT: } -# INVALID_ARCH1-NEXT: } -# INVALID_ARCH1-NEXT: } .section .riscv.attributes,"",@0x70000003 .byte 0x41 .long .Lend-.riscv.attributes-1 Index: llvm/include/llvm/Support/RISCVISAInfo.h =================================================================== --- llvm/include/llvm/Support/RISCVISAInfo.h +++ llvm/include/llvm/Support/RISCVISAInfo.h @@ -51,6 +51,12 @@ bool ExperimentalExtensionVersionCheck = true, bool IgnoreUnknown = false); + /// Parse RISCV ISA info from an arch string that is already in normalized + /// form (as defined in the psABI). Unlike parseArchString, this function + /// will not error for unrecognized extension names or extension versions. + static llvm::Expected> + parseNormalizedArchString(StringRef Arch); + /// Parse RISCV ISA info from feature vector. static llvm::Expected> parseFeatures(unsigned XLen, const std::vector &Features); Index: llvm/lib/Support/RISCVISAInfo.cpp =================================================================== --- llvm/lib/Support/RISCVISAInfo.cpp +++ llvm/lib/Support/RISCVISAInfo.cpp @@ -512,6 +512,67 @@ return RISCVISAInfo::postProcessAndChecking(std::move(ISAInfo)); } +llvm::Expected> +RISCVISAInfo::parseNormalizedArchString(StringRef Arch) { + if (llvm::any_of(Arch, isupper)) { + return createStringError(errc::invalid_argument, + "string must be lowercase"); + } + // Must start with a valid base ISA name. + unsigned XLen; + if (Arch.startswith("rv32i") || Arch.startswith("rv32e")) + XLen = 32; + else if (Arch.startswith("rv64i") || Arch.startswith("rv64e")) + XLen = 64; + else + return createStringError(errc::invalid_argument, + "arch string must begin with valid base ISA"); + std::unique_ptr ISAInfo(new RISCVISAInfo(XLen)); + // Discard rv32/rv64 prefix. + Arch = Arch.substr(4); + + // Each extension is of the form ${name}${major_version}p${minor_version} + // and separated by _. Split by _ and then extract the name and version + // information for each extension. + SmallVector Split; + Arch.split(Split, '_'); + for (StringRef Ext : Split) { + StringRef Prefix, MinorVersionStr; + std::tie(Prefix, MinorVersionStr) = Ext.rsplit('p'); + if (MinorVersionStr.empty()) + return createStringError(errc::invalid_argument, + "extension lacks version in expected format"); + unsigned long long MajorVersion, MinorVersion; + if (getAsUnsignedInteger(MinorVersionStr, 10, MinorVersion)) + return createStringError(errc::invalid_argument, + "failed to parse minor version number"); + + // Split Prefix into the extension name and the major version number + // (the trailing digits of Prefix). + int TrailingDigits = 0; + StringRef ExtName = Prefix; + while (!ExtName.empty()) { + if (!isDigit(ExtName.back())) + break; + ExtName = ExtName.drop_back(1); + TrailingDigits++; + } + if (!TrailingDigits) + return createStringError(errc::invalid_argument, + "failed to find major version number"); + + StringRef MajorVersionStr = Prefix.take_back(TrailingDigits); + if (getAsUnsignedInteger(MajorVersionStr, 10, MajorVersion)) + return createStringError(errc::invalid_argument, + "failed to parse major version number"); + ISAInfo->addExtension(ExtName, MajorVersion, MinorVersion); + } + ISAInfo->updateFLen(); + ISAInfo->updateMinVLen(); + ISAInfo->updateMaxELen(); + return std::move(ISAInfo); +} + llvm::Expected> RISCVISAInfo::parseArchString(StringRef Arch, bool EnableExperimentalExtension, bool ExperimentalExtensionVersionCheck,