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 @@ -179,6 +179,11 @@ bool parseDirectiveAttribute(); bool parseDirectiveInsn(SMLoc L); + /// 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().getFeatureBits()[Feature])) { MCSubtargetInfo &STI = copySTI(); @@ -2047,6 +2052,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. @@ -2085,6 +2126,70 @@ return false; } + if (Option == "arch") { + + Parser.Lex(); + + Parser.parseComma(); + + if (Parser.parseOptionalToken(AsmToken::Equal)) { + SMLoc ArchLoc = Parser.getTok().getLoc(); + if (Parser.getTok().isNot(AsmToken::Identifier)) + return Error(ArchLoc, "unexpected token, expected identifier"); + + StringRef Arch = Parser.getTok().getString(); + std::string Result; + if (resetToArch(Arch, ArchLoc, Result)) + return true; + + Parser.Lex(); + getTargetStreamer().emitDirectiveOptionArchEqual(Result); + + return Parser.parseToken(AsmToken::EndOfStatement, + "unexpected token, expected end of statement"); + } + + bool IsAdd; + if (Parser.getTok().is(AsmToken::Plus)) + IsAdd = true; + else if (Parser.getTok().is(AsmToken::Minus)) + IsAdd = false; + else + return Error(Parser.getTok().getLoc(), + "unexpected token, expected '+', '-' or '=' to add, " + "remove extension or reset features"); + + Parser.Lex(); + + if (Parser.getTok().isNot(AsmToken::Identifier)) + return Error(Parser.getTok().getLoc(), + "unexpected token, expected identifier"); + + StringRef Extension = Parser.getTok().getString(); + + // TODO: Enable or disable extension according to extension and extension + // version number. Currently extension version number is not supported. + if (Extension.find_if(isDigit) != StringRef::npos) + return Error(Parser.getTok().getLoc(), + "extension version number is not yet supported"); + + for (auto Feature : RISCVFeatureKV) { + if (Extension == Feature.Key) { + if (IsAdd) { + setFeatureBits(Feature.Value, Feature.Key); + getTargetStreamer().emitDirectiveOptionArchPlus(Extension); + } else { + clearFeatureBits(Feature.Value, Feature.Key); + getTargetStreamer().emitDirectiveOptionArchMinus(Extension); + } + Parser.Lex(); + return Parser.parseToken(AsmToken::EndOfStatement, + "unexpected token, expected end of statement"); + } + } + return Error(Parser.getTok().getLoc(), "unknown extension"); + } + if (Option == "rvc") { getTargetStreamer().emitDirectiveOptionRVC(); @@ -2158,9 +2263,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; } @@ -2236,39 +2341,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 @@ -32,6 +32,9 @@ virtual void emitDirectiveOptionNoRVC(); virtual void emitDirectiveOptionRelax(); virtual void emitDirectiveOptionNoRelax(); + virtual void emitDirectiveOptionArchEqual(StringRef Value); + virtual void emitDirectiveOptionArchPlus(StringRef Value); + virtual void emitDirectiveOptionArchMinus(StringRef Value); virtual void emitAttribute(unsigned Attribute, unsigned Value); virtual void finishAttributeSection(); virtual void emitTextAttribute(unsigned Attribute, StringRef String); @@ -64,6 +67,9 @@ void emitDirectiveOptionNoRVC() override; void emitDirectiveOptionRelax() override; void emitDirectiveOptionNoRelax() override; + void emitDirectiveOptionArchEqual(StringRef Value) override; + void emitDirectiveOptionArchPlus(StringRef Value) override; + void emitDirectiveOptionArchMinus(StringRef Value) 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 @@ -31,6 +31,9 @@ void RISCVTargetStreamer::emitDirectiveOptionNoRVC() {} void RISCVTargetStreamer::emitDirectiveOptionRelax() {} void RISCVTargetStreamer::emitDirectiveOptionNoRelax() {} +void RISCVTargetStreamer::emitDirectiveOptionArchEqual(StringRef Value) {} +void RISCVTargetStreamer::emitDirectiveOptionArchPlus(StringRef Value) {} +void RISCVTargetStreamer::emitDirectiveOptionArchMinus(StringRef Value) {} void RISCVTargetStreamer::emitAttribute(unsigned Attribute, unsigned Value) {} void RISCVTargetStreamer::finishAttributeSection() {} void RISCVTargetStreamer::emitTextAttribute(unsigned Attribute, @@ -109,4 +112,14 @@ unsigned IntValue, StringRef StringValue) {} +void RISCVTargetAsmStreamer::emitDirectiveOptionArchEqual(StringRef Value) { + OS << "\t .option\tarch,\t=" << Value << "\"\n"; +} +void RISCVTargetAsmStreamer::emitDirectiveOptionArchPlus(StringRef Value) { + OS << "\t .option\tarch,\t+" << Value << "\"\n"; +} +void RISCVTargetAsmStreamer::emitDirectiveOptionArchMinus(StringRef Value) { + OS << "\t .option\tarch,\t-" << Value << "\"\n"; +} + 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,97 @@ +# 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 @@ -10,10 +10,28 @@ # CHECK: error: unexpected token, expected identifier .option "str" +# CHECK: error: unexpected token, expected identifier +.option arch, ="rv32i" + +# CHECK: error: unexpected token, expected identifier +.option arch, +"c" + +# CHECK: error: unknown extension +.option arch, +x + +# CHECK: error: unexpected token, expected end of statement +.option arch, =rv32i foo + +# CHECK: error: unexpected token, expected end of statement +.option arch, +c foo + +# CHECK: error: extension version number is not yet supported +.option arch, +c2p0 + # CHECK: error: unexpected token, expected end of statement .option rvc foo -# CHECK: warning: unknown option, expected 'push', 'pop', 'rvc', 'norvc', 'relax' or 'norelax' +# CHECK: warning: unknown option, expected 'push', 'pop', 'rvc', 'norvc', 'arch', 'relax' or 'norelax' .option bar # CHECK: error: .option pop with no .option push