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 @@ -182,6 +182,11 @@ bool parseDirectiveInsn(SMLoc L); bool parseDirectiveVariantCC(); + /// Helper to reset target features for a new arch string. It + /// also records the new arch string that expanded by RISCVISAInfo + /// and reports error for invalid arch string + bool resetToArch(StringRef Arch, SMLoc Loc, std::string &Result); + void setFeatureBits(uint64_t Feature, StringRef FeatureString) { if (!(getSTI().hasFeature(Feature))) { MCSubtargetInfo &STI = copySTI(); @@ -2117,6 +2122,42 @@ return true; } +bool RISCVAsmParser::resetToArch(StringRef Arch, SMLoc Loc, + std::string &Result) { + for (auto Feature : RISCVFeatureKV) + if (llvm::RISCVISAInfo::isSupportedExtensionFeature(Feature.Key)) + clearFeatureBits(Feature.Value, Feature.Key); + + auto ParseResult = llvm::RISCVISAInfo::parseArchString( + Arch, /*EnableExperimentalExtension=*/true, + /*ExperimentalExtensionVersionCheck=*/true); + if (!ParseResult) { + std::string Buffer; + raw_string_ostream OutputErrMsg(Buffer); + handleAllErrors(ParseResult.takeError(), [&](llvm::StringError &ErrMsg) { + OutputErrMsg << "invalid arch name '" << Arch << "', " + << ErrMsg.getMessage(); + }); + + return Error(Loc, OutputErrMsg.str()); + } + auto &ISAInfo = *ParseResult; + + for (auto Feature : RISCVFeatureKV) + if (ISAInfo->hasExtension(Feature.Key)) + setFeatureBits(Feature.Value, Feature.Key); + + if (ISAInfo->getXLen() == 32) + clearFeatureBits(RISCV::Feature64Bit, "64bit"); + else if (ISAInfo->getXLen() == 64) + setFeatureBits(RISCV::Feature64Bit, "64bit"); + else + return Error(Loc, "bad arch string " + Arch); + + Result = ISAInfo->toString(); + return false; +} + bool RISCVAsmParser::parseDirectiveOption() { MCAsmParser &Parser = getParser(); // Get the option token. @@ -2149,6 +2190,105 @@ return false; } + if (Option == "arch") { + + Parser.parseComma(); + + bool PrefixEmitted = false; + bool IsExtensionList = false; + bool IsFullArchString = false; + while (true) { + bool IsAdd; + if (Parser.getTok().is(AsmToken::Plus)) { + IsAdd = true; + IsExtensionList = true; + } else if (Parser.getTok().is(AsmToken::Minus)) { + IsAdd = false; + IsExtensionList = true; + } else { + IsFullArchString = true; + SMLoc ArchLoc = Parser.getTok().getLoc(); + + if (IsExtensionList) + return Error( + ArchLoc, + "mix extension list with full arch string is not allowed!"); + + if (Parser.getTok().isNot(AsmToken::String)) + return Error(ArchLoc, "unexpected token, expected string"); + + StringRef Arch = Parser.getTok().getStringContents(); + std::string Result; + if (resetToArch(Arch, ArchLoc, Result)) + return true; + + Parser.Lex(); + bool HasComma = getTok().is(AsmToken::Comma); + getTargetStreamer().emitDirectiveOptionArchEqual(Result, PrefixEmitted, + HasComma); + + if (HasComma) { + Parser.Lex(); + continue; + } + return Parser.parseToken(AsmToken::EndOfStatement, + "unexpected token, expected end of statement"); + } + + if (IsFullArchString) + return Error( + Parser.getTok().getLoc(), + "mix extension list with full arch string is not allowed!"); + + Parser.Lex(); + + if (Parser.getTok().isNot(AsmToken::Identifier)) + return Error(Parser.getTok().getLoc(), + "unexpected token, expected identifier"); + + StringRef ExtStr = Parser.getTok().getString(); + + if (ExtStr.find_if(isDigit) != StringRef::npos) + return Error(Parser.getTok().getLoc(), + "Don't specify version number, extensions don't rely on " + "version numbers"); + + ArrayRef KVArray(RISCVFeatureKV); + auto Ext = llvm::lower_bound(KVArray, ExtStr); + if (Ext == KVArray.end() || StringRef(Ext->Key) != ExtStr) + return Error(Parser.getTok().getLoc(), "unknown extension"); + + Parser.Lex(); // Eat arch string + bool HasComma = getTok().is(AsmToken::Comma); + if (IsAdd) { + setFeatureBits(Ext->Value, Ext->Key); + getTargetStreamer().emitDirectiveOptionArchPlus(Ext->Key, PrefixEmitted, + HasComma); + } else { + // Check dependency. It is invalid to disable an extension that + // there are other enabled extensions depend on it. + for (auto Feature : KVArray) { + if (getSTI().hasFeature(Feature.Value) && + Feature.Implies.test(Ext->Value)) + return Error(Parser.getTok().getLoc(), + Twine("Can't disable ") + Ext->Key + " extension, " + + Feature.Key + " extension requires " + Ext->Key + + " extension be enabled"); + } + + clearFeatureBits(Ext->Value, Ext->Key); + getTargetStreamer().emitDirectiveOptionArchMinus( + Ext->Key, PrefixEmitted, HasComma); + } + + if (!HasComma) + return Parser.parseToken(AsmToken::EndOfStatement, + "unexpected token, expected end of statement"); + // Eat comma + Parser.Lex(); + } + } + if (Option == "rvc") { if (Parser.parseEOL()) return true; @@ -2205,9 +2345,9 @@ } // Unknown option. - Warning(Parser.getTok().getLoc(), - "unknown option, expected 'push', 'pop', 'rvc', 'norvc', 'relax' or " - "'norelax'"); + Warning(Parser.getTok().getLoc(), "unknown option, expected 'push', 'pop', " + "'rvc', 'norvc', 'arch', 'relax' or " + "'norelax'"); Parser.eatToEndOfStatement(); return false; } @@ -2282,39 +2422,12 @@ else if (Tag != RISCVAttrs::ARCH) getTargetStreamer().emitTextAttribute(Tag, StringValue); else { - StringRef Arch = StringValue; - for (auto Feature : RISCVFeatureKV) - if (llvm::RISCVISAInfo::isSupportedExtensionFeature(Feature.Key)) - clearFeatureBits(Feature.Value, Feature.Key); - - auto ParseResult = llvm::RISCVISAInfo::parseArchString( - StringValue, /*EnableExperimentalExtension=*/true, - /*ExperimentalExtensionVersionCheck=*/true); - if (!ParseResult) { - std::string Buffer; - raw_string_ostream OutputErrMsg(Buffer); - handleAllErrors(ParseResult.takeError(), [&](llvm::StringError &ErrMsg) { - OutputErrMsg << "invalid arch name '" << Arch << "', " - << ErrMsg.getMessage(); - }); - - return Error(ValueExprLoc, OutputErrMsg.str()); - } - auto &ISAInfo = *ParseResult; - - for (auto Feature : RISCVFeatureKV) - if (ISAInfo->hasExtension(Feature.Key)) - setFeatureBits(Feature.Value, Feature.Key); - - if (ISAInfo->getXLen() == 32) - clearFeatureBits(RISCV::Feature64Bit, "64bit"); - else if (ISAInfo->getXLen() == 64) - setFeatureBits(RISCV::Feature64Bit, "64bit"); - else - return Error(ValueExprLoc, "bad arch string " + Arch); + std::string Result; + if (resetToArch(StringValue, ValueExprLoc, Result)) + return true; // Then emit the arch string. - getTargetStreamer().emitTextAttribute(Tag, ISAInfo->toString()); + getTargetStreamer().emitTextAttribute(Tag, Result); } return false; 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 @@ -34,6 +34,14 @@ virtual void emitDirectiveOptionRelax(); virtual void emitDirectiveOptionNoRelax(); virtual void emitDirectiveVariantCC(MCSymbol &Symbol); + virtual void emitDirectiveOptionArchEqual(StringRef Value, + bool &PrefixEmitted, + bool EmitComma); + virtual void emitDirectiveOptionArchPlus(StringRef Value, bool &PrefixEmitted, + bool EmitComma); + virtual void emitDirectiveOptionArchMinus(StringRef Value, + bool &PrefixEmitted, + bool EmitComma); virtual void emitAttribute(unsigned Attribute, unsigned Value); virtual void finishAttributeSection(); virtual void emitTextAttribute(unsigned Attribute, StringRef String); @@ -67,6 +75,12 @@ void emitDirectiveOptionRelax() override; void emitDirectiveOptionNoRelax() override; void emitDirectiveVariantCC(MCSymbol &Symbol) override; + void emitDirectiveOptionArchEqual(StringRef Value, bool &PrefixEmitted, + bool EmitComma) override; + void emitDirectiveOptionArchPlus(StringRef Value, bool &PrefixEmitted, + bool EmitComma) override; + void emitDirectiveOptionArchMinus(StringRef Value, bool &PrefixEmitted, + bool EmitComma) override; }; } 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 @@ -34,6 +34,15 @@ void RISCVTargetStreamer::emitDirectiveOptionRelax() {} void RISCVTargetStreamer::emitDirectiveOptionNoRelax() {} void RISCVTargetStreamer::emitDirectiveVariantCC(MCSymbol &Symbol) {} +void RISCVTargetStreamer::emitDirectiveOptionArchEqual(StringRef Value, + bool &hasDotOption, + bool EmitComma) {} +void RISCVTargetStreamer::emitDirectiveOptionArchPlus(StringRef Value, + bool &hasDotOption, + bool EmitComma) {} +void RISCVTargetStreamer::emitDirectiveOptionArchMinus(StringRef Value, + bool &hasDotOption, + bool EmitComma) {} void RISCVTargetStreamer::emitAttribute(unsigned Attribute, unsigned Value) {} void RISCVTargetStreamer::finishAttributeSection() {} void RISCVTargetStreamer::emitTextAttribute(unsigned Attribute, @@ -116,4 +125,41 @@ unsigned IntValue, StringRef StringValue) {} +static void emitDirectiveOptionArchPrefix(formatted_raw_ostream &OS, + bool &PrefixEmitted) { + if (!PrefixEmitted) { + OS << "\t .option\tarch,\t"; + PrefixEmitted = true; + } +} + +static void emitCommaOrNextLine(formatted_raw_ostream &OS, bool EmitComma) { + if (EmitComma) + OS << ", "; + else + OS << "\n"; +} + +void RISCVTargetAsmStreamer::emitDirectiveOptionArchEqual(StringRef Value, + bool &PrefixEmitted, + bool EmitComma) { + emitDirectiveOptionArchPrefix(OS, PrefixEmitted); + OS << "\"" << Value << "\""; + emitCommaOrNextLine(OS, EmitComma); +} +void RISCVTargetAsmStreamer::emitDirectiveOptionArchPlus(StringRef Value, + bool &PrefixEmitted, + bool EmitComma) { + emitDirectiveOptionArchPrefix(OS, PrefixEmitted); + OS << "+" << Value; + emitCommaOrNextLine(OS, EmitComma); +} +void RISCVTargetAsmStreamer::emitDirectiveOptionArchMinus(StringRef Value, + bool &PrefixEmitted, + bool EmitComma) { + emitDirectiveOptionArchPrefix(OS, PrefixEmitted); + OS << "-" << Value; + emitCommaOrNextLine(OS, EmitComma); +} + void RISCVTargetAsmStreamer::finishAttributeSection() {} diff --git a/llvm/test/MC/RISCV/option-arch.s b/llvm/test/MC/RISCV/option-arch.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/RISCV/option-arch.s @@ -0,0 +1,96 @@ +# RUN: llvm-mc -triple riscv32 -show-encoding < %s \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv32 -show-encoding \ +# RUN: -riscv-no-aliases < %s | FileCheck -check-prefixes=CHECK,CHECK-INST %s +# RUN: llvm-mc -triple riscv32 -filetype=obj < %s \ +# RUN: | llvm-objdump --triple=riscv32 --mattr=+c -d - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv32 -filetype=obj < %s \ +# RUN: | llvm-objdump --triple=riscv32 --mattr=+c -d -M no-aliases - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s + +# RUN: llvm-mc -triple riscv64 -show-encoding < %s \ +# RUN: | FileCheck -check-prefixes=CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv64 -show-encoding \ +# RUN: -riscv-no-aliases < %s | FileCheck -check-prefixes=CHECK-INST %s +# RUN: llvm-mc -triple riscv64 -filetype=obj < %s \ +# RUN: | llvm-objdump --triple=riscv64 --mattr=+c -d - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv64 -filetype=obj < %s \ +# RUN: | llvm-objdump --triple=riscv64 --mattr=+c -d -M no-aliases - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s + +# Test '.option arch, +' and '.option arch, -' directive +# The following test cases were copied from MC/RISCV/option-rvc.s + +# CHECK-BYTES: 13 85 05 00 +# CHECK-ALIAS: mv a0, a1 +# CHECK-INST: addi a0, a1, 0 +# CHECK: # encoding: [0x13,0x85,0x05,0x00] +addi a0, a1, 0 + +# CHECK-BYTES: 13 04 c1 3f +# CHECK-ALIAS: addi s0, sp, 1020 +# CHECK-INST: addi s0, sp, 1020 +# CHECK: # encoding: [0x13,0x04,0xc1,0x3f] +addi s0, sp, 1020 + +# CHECK: .option arch, +c +.option arch, +c +# CHECK-BYTES: 2e 85 +# CHECK-ALIAS: mv a0, a1 +# CHECK-INST: c.mv a0, a1 +# CHECK: # encoding: [0x2e,0x85] +addi a0, a1, 0 + +# CHECK-BYTES: e0 1f +# CHECK-ALIAS: addi s0, sp, 1020 +# CHECK-INST: c.addi4spn s0, sp, 1020 +# CHECK: # encoding: [0xe0,0x1f] +addi s0, sp, 1020 + +# CHECK: .option arch, -c +.option arch, -c +# CHECK-BYTES: 13 85 05 00 +# CHECK-ALIAS: mv a0, a1 +# CHECK-INST: addi a0, a1, 0 +# CHECK: # encoding: [0x13,0x85,0x05,0x00] +addi a0, a1, 0 + +# CHECK-BYTES: 13 04 c1 3f +# CHECK-ALIAS: addi s0, sp, 1020 +# CHECK-INST: addi s0, sp, 1020 +# CHECK: # encoding: [0x13,0x04,0xc1,0x3f] +addi s0, sp, 1020 + +# CHECK: .option arch, +c +.option arch, +c +# CHECK-BYTES: 2e 85 +# CHECK-ALIAS: mv a0, a1 +# CHECK-INST: c.mv a0, a1 +# CHECK: # encoding: [0x2e,0x85] +addi a0, a1, 0 + +# CHECK-BYTES: e0 1f +# CHECK-ALIAS: addi s0, sp, 1020 +# CHECK-INST: c.addi4spn s0, sp, 1020 +# CHECK: # encoding: [0xe0,0x1f] +addi s0, sp, 1020 + +# CHECK: .option arch, -c +.option arch, -c +# CHECK-BYTES: 13 85 05 00 +# CHECK-ALIAS: mv a0, a1 +# CHECK-INST: addi a0, a1, 0 +# CHECK: # encoding: [0x13,0x85,0x05,0x00] +addi a0, a1, 0 + +# CHECK-BYTES: 13 04 c1 3f +# CHECK-ALIAS: addi s0, sp, 1020 +# CHECK-INST: addi s0, sp, 1020 +# CHECK: # encoding: [0x13,0x04,0xc1,0x3f] +addi s0, sp, 1020 + +# Test '.option arch, ' directive +#CHECK: .option arch, "rv32i2p0_m2p0_a2p0_c2p0" +.option arch, "rv32i2p0_m2p0_a2p0_c2p0" diff --git a/llvm/test/MC/RISCV/option-invalid.s b/llvm/test/MC/RISCV/option-invalid.s --- a/llvm/test/MC/RISCV/option-invalid.s +++ b/llvm/test/MC/RISCV/option-invalid.s @@ -13,7 +13,35 @@ # CHECK: :[[#@LINE+1]]:13: error: expected newline .option rvc foo -# CHECK: :[[#@LINE+1]]:12: warning: unknown option, expected 'push', 'pop', 'rvc', 'norvc', 'relax' or 'norelax' +# CHECK: :[[#@LINE+1]]:15: error: unexpected token, expected string +.option arch, rv32i + +# CHECK: :[[#@LINE+1]]:23: error: mix extension list with full arch string is not allowed! +.option arch, +f, +d, "rv32ifd", -d + +# CHECK: :[[#@LINE+1]]:16: error: unexpected token, expected identifier +.option arch, +"c" + +# CHECK: :[[#@LINE+1]]:16: error: unknown extension +.option arch, +x + +# CHECK: :[[#@LINE+1]]:23: error: unexpected token, expected end of statement +.option arch, "rv32i" foo + +# CHECK: :[[#@LINE+1]]:18: error: unexpected token, expected end of statement +.option arch, +c foo + +# CHECK: :[[#@LINE+1]]:16: error: Don't specify version number, extensions don't rely on version numbers +.option arch, +c2p0 + +.option arch, +d +# CHECK: :[[#@LINE+1]]:17: error: Can't disable f extension, d extension requires f extension be enabled +.option arch, -f + +# CHECK: :[[#@LINE+1]]:13: error: expected newline +.option rvc foo + +# CHECK: :[[#@LINE+1]]:12: warning: unknown option, expected 'push', 'pop', 'rvc', 'norvc', 'arch', 'relax' or 'norelax' .option bar # CHECK: :[[#@LINE+1]]:12: error: .option pop with no .option push