Index: lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -15,6 +15,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCParsedAsmOperand.h" #include "llvm/MC/MCParser/MCTargetAsmParser.h" @@ -22,8 +23,11 @@ #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/TargetRegistry.h" +#include + using namespace llvm; namespace { @@ -51,6 +55,12 @@ bool ParseDirective(AsmToken DirectiveID) override; + bool processInstruction(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + void emitLoadImm(unsigned int DestReg, int64_t Value, MCStreamer &Out, + const MCSubtargetInfo *STI); + // Auto-generated instruction matching functions #define GET_ASSEMBLER_HEADER #include "RISCVGenAsmMatcher.inc" @@ -206,6 +216,16 @@ return RISCVFPRndMode::stringToRoundingMode(Str) != RISCVFPRndMode::Invalid; } + bool isSImmXLen() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + bool IsInRange = isRV64() ? true : isInt<32>(Imm); + return IsConstantImm && IsInRange && VK == RISCVMCExpr::VK_RISCV_None; + } + bool isUImmLog2XLen() const { int64_t Imm; RISCVMCExpr::VariantKind VK; @@ -581,9 +601,7 @@ default: break; case Match_Success: - Inst.setLoc(IDLoc); - Out.EmitInstruction(Inst, getSTI()); - return false; + return processInstruction(Inst, IDLoc, Out, STI); case Match_MissingFeature: return Error(IDLoc, "instruction use requires an option to be enabled"); case Match_MnemonicFail: @@ -600,6 +618,11 @@ } return Error(ErrorLoc, "invalid operand for instruction"); } + case Match_InvalidSImmXLen: + assert(!isRV64() && "immediate can not be out of range on RV64"); + return generateImmOutOfRangeError(Operands, ErrorInfo, + std::numeric_limits::min(), + std::numeric_limits::max()); case Match_InvalidUImmLog2XLen: if (isRV64()) return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 6) - 1); @@ -938,6 +961,53 @@ return Kind != RISCVMCExpr::VK_RISCV_Invalid; } +bool RISCVAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + Inst.setLoc(IDLoc); + + switch (Inst.getOpcode()) { + case RISCV::PseudoLI: { + const MCOperand &DstRegOp = Inst.getOperand(0); + const MCOperand &ImmOp = Inst.getOperand(1); + if (!isInt<32>(ImmOp.getImm())) + return Error(IDLoc, "li for immediates with more than 32 bits is " + "currently not implemented"); + emitLoadImm(DstRegOp.getReg(), ImmOp.getImm(), Out, STI); + return false; + } + } + + Out.EmitInstruction(Inst, *STI); + return false; +} + +// FIXME This method is currently a 1:1 copy of RISCVAsmPrinter::emitLoadImm. +// Where can we place a shared version? +void RISCVAsmParser::emitLoadImm(unsigned int DestReg, int64_t Value, + MCStreamer &Out, const MCSubtargetInfo *STI) { + assert(isInt<32>(Value) && + "Immediates with more than 32 bits are currently not implemented."); + int64_t Hi20 = ((Value + 0x800) >> 12) & 0xFFFFF; + int64_t Lo12 = SignExtend64<12>(Value & 0xFFF); + if (Hi20 && Lo12) { + Out.EmitInstruction(MCInstBuilder(RISCV::LUI).addReg(DestReg).addImm(Hi20), + *STI); + Out.EmitInstruction( + MCInstBuilder(RISCV::ADDI).addReg(DestReg).addReg(DestReg).addImm(Lo12), + *STI); + } else if (Hi20) { + Out.EmitInstruction(MCInstBuilder(RISCV::LUI).addReg(DestReg).addImm(Hi20), + *STI); + } else { + Out.EmitInstruction(MCInstBuilder(RISCV::ADDI) + .addReg(DestReg) + .addReg(RISCV::X0) + .addImm(Lo12), + *STI); + } +} + bool RISCVAsmParser::ParseDirective(AsmToken DirectiveID) { return true; } extern "C" void LLVMInitializeRISCVAsmParser() { Index: lib/Target/RISCV/RISCVAsmPrinter.cpp =================================================================== --- lib/Target/RISCV/RISCVAsmPrinter.cpp +++ lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -22,6 +22,7 @@ #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/TargetRegistry.h" @@ -32,6 +33,9 @@ namespace { class RISCVAsmPrinter : public AsmPrinter { + void emitLoadImm(unsigned int DestReg, int64_t Value, MCStreamer &Out, + const MCSubtargetInfo *STI); + public: explicit RISCVAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) @@ -67,6 +71,14 @@ if (emitPseudoExpansionLowering(*OutStreamer, MI)) return; + if (MI->getOpcode() == RISCV::PseudoLI) { + const MachineOperand &DstRegOp = MI->getOperand(0); + const MachineOperand &ImmOp = MI->getOperand(1); + emitLoadImm(DstRegOp.getReg(), ImmOp.getImm(), *OutStreamer, + &getSubtargetInfo()); + return; + } + MCInst TmpInst; LowerRISCVMachineInstrToMCInst(MI, TmpInst, *this); EmitToStreamer(*OutStreamer, TmpInst); @@ -120,6 +132,32 @@ return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, AsmVariant, ExtraCode, OS); } +// FIXME This method is currently a 1:1 copy of RISCVAsmParser::emitLoadImm. +// Where can we place a shared version? +void RISCVAsmPrinter::emitLoadImm(unsigned int DestReg, int64_t Value, + MCStreamer &Out, const MCSubtargetInfo *STI) { + assert(isInt<32>(Value) && + "Immediates with more than 32 bits are currently not implemented."); + int64_t Hi20 = ((Value + 0x800) >> 12) & 0xFFFFF; + int64_t Lo12 = SignExtend64<12>(Value & 0xFFF); + if (Hi20 && Lo12) { + Out.EmitInstruction(MCInstBuilder(RISCV::LUI).addReg(DestReg).addImm(Hi20), + *STI); + Out.EmitInstruction( + MCInstBuilder(RISCV::ADDI).addReg(DestReg).addReg(DestReg).addImm(Lo12), + *STI); + } else if (Hi20) { + Out.EmitInstruction(MCInstBuilder(RISCV::LUI).addReg(DestReg).addImm(Hi20), + *STI); + } else { + Out.EmitInstruction(MCInstBuilder(RISCV::ADDI) + .addReg(DestReg) + .addReg(RISCV::X0) + .addImm(Lo12), + *STI); + } +} + // Force static initialization. extern "C" void LLVMInitializeRISCVAsmPrinter() { RegisterAsmPrinter X(getTheRISCV32Target()); Index: lib/Target/RISCV/RISCVInstrFormats.td =================================================================== --- lib/Target/RISCV/RISCVInstrFormats.td +++ lib/Target/RISCV/RISCVInstrFormats.td @@ -102,8 +102,8 @@ } // Pseudo instructions -class Pseudo pattern> - : RVInst { +class Pseudo pattern, string opcodestr = "", string argstr = ""> + : RVInst { let isPseudo = 1; let isCodeGenOnly = 1; } Index: lib/Target/RISCV/RISCVInstrInfo.cpp =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.cpp +++ lib/Target/RISCV/RISCVInstrInfo.cpp @@ -83,17 +83,8 @@ MachineInstr::MIFlag Flag) const { assert(isInt<32>(Val) && "Can only materialize 32-bit constants"); - // TODO: If the value can be materialized using only one instruction, only - // insert a single instruction. - - uint64_t Hi20 = ((Val + 0x800) >> 12) & 0xfffff; - uint64_t Lo12 = SignExtend64<12>(Val); - BuildMI(MBB, MBBI, DL, get(RISCV::LUI), DstReg) - .addImm(Hi20) - .setMIFlag(Flag); - BuildMI(MBB, MBBI, DL, get(RISCV::ADDI), DstReg) - .addReg(DstReg, RegState::Kill) - .addImm(Lo12) + BuildMI(MBB, MBBI, DL, get(RISCV::PseudoLI), DstReg) + .addImm(Val) .setMIFlag(Flag); } Index: lib/Target/RISCV/RISCVInstrInfo.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.td +++ lib/Target/RISCV/RISCVInstrInfo.td @@ -43,6 +43,12 @@ // Operand and SDNode transformation definitions. //===----------------------------------------------------------------------===// +class ImmXLenAsmOperand : AsmOperandClass { + let Name = prefix # "ImmXLen" # suffix; + let RenderMethod = "addImmOperands"; + let DiagnosticType = !strconcat("Invalid", Name); +} + class ImmAsmOperand : AsmOperandClass { let Name = prefix # "Imm" # width # suffix; let RenderMethod = "addImmOperands"; @@ -122,7 +128,9 @@ } // A parameterized register class alternative to i32imm/i64imm from Target.td. -def ixlenimm : Operand; +def ixlenimm : Operand { + let ParserMatchClass = ImmXLenAsmOperand<"S">; +} // Standalone (codegen-only) immleaf patterns. def simm32 : ImmLeaf(Imm);}]>; @@ -386,7 +394,11 @@ // TODO RV64I: sd def : InstAlias<"nop", (ADDI X0, X0, 0)>; -// TODO li + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0, + isCodeGenOnly = 0, isAsmParserOnly = 1 in +def PseudoLI : Pseudo<(outs GPR:$rd), (ins ixlenimm:$imm), [], "li", "$rd, $imm">; + def : InstAlias<"mv $rd, $rs", (ADDI GPR:$rd, GPR:$rs, 0)>; def : InstAlias<"not $rd, $rs", (XORI GPR:$rd, GPR:$rs, -1)>; def : InstAlias<"neg $rd, $rs", (SUB GPR:$rd, X0, GPR:$rs)>; Index: test/MC/RISCV/rv32i-aliases-invalid.s =================================================================== --- test/MC/RISCV/rv32i-aliases-invalid.s +++ test/MC/RISCV/rv32i-aliases-invalid.s @@ -4,5 +4,7 @@ # TODO ld # TODO sd +li x0, 0x100000000 # CHECK: :[[@LINE]]:8: error: immediate must be an integer in the range [-2147483648, 2147483647] + negw x1, x2 # CHECK: :[[@LINE]]:1: error: instruction use requires an option to be enabled sext.w x3, x4 # CHECK: :[[@LINE]]:1: error: instruction use requires an option to be enabled Index: test/MC/RISCV/rv64i-aliases-invalid.s =================================================================== --- test/MC/RISCV/rv64i-aliases-invalid.s +++ test/MC/RISCV/rv64i-aliases-invalid.s @@ -1,6 +1,8 @@ # RUN: not llvm-mc %s -triple=riscv64 -riscv-no-aliases 2>&1 | FileCheck %s # RUN: not llvm-mc %s -triple=riscv64 2>&1 | FileCheck %s +li x0, 0x100000000 # CHECK: :[[@LINE]]:1: error: li for immediates with more than 32 bits is currently not implemented + rdinstreth x29 # CHECK: :[[@LINE]]:1: error: instruction use requires an option to be enabled rdcycleh x27 # CHECK: :[[@LINE]]:1: error: instruction use requires an option to be enabled rdtimeh x28 # CHECK: :[[@LINE]]:1: error: instruction use requires an option to be enabled Index: test/MC/RISCV/rvi-aliases-valid.s =================================================================== --- test/MC/RISCV/rvi-aliases-valid.s +++ test/MC/RISCV/rvi-aliases-valid.s @@ -1,23 +1,28 @@ # RUN: llvm-mc %s -triple=riscv32 -riscv-no-aliases \ -# RUN: | FileCheck -check-prefix=CHECK-INST %s +# RUN: | FileCheck -check-prefixes=CHECK-EXPAND,CHECK-INST %s # RUN: llvm-mc %s -triple=riscv32 \ -# RUN: | FileCheck -check-prefix=CHECK-ALIAS %s +# RUN: | FileCheck -check-prefixes=CHECK-EXPAND,CHECK-ALIAS %s # RUN: llvm-mc %s -triple=riscv64 -riscv-no-aliases\ -# RUN: | FileCheck -check-prefix=CHECK-INST %s +# RUN: | FileCheck -check-prefixes=CHECK-EXPAND,CHECK-INST %s # RUN: llvm-mc %s -triple=riscv64 \ -# RUN: | FileCheck -check-prefix=CHECK-ALIAS %s +# RUN: | FileCheck -check-prefixes=CHECK-EXPAND,CHECK-ALIAS %s # RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ # RUN: | llvm-objdump -d -riscv-no-aliases - \ -# RUN: | FileCheck -check-prefix=CHECK-INST %s +# RUN: | FileCheck -check-prefixes=CHECK-EXPAND,CHECK-INST %s # RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ # RUN: | llvm-objdump -d - \ -# RUN: | FileCheck -check-prefix=CHECK-ALIAS %s +# RUN: | FileCheck -check-prefixes=CHECK-EXPAND,CHECK-ALIAS %s # RUN: llvm-mc -filetype=obj -triple riscv64 < %s \ # RUN: | llvm-objdump -d -riscv-no-aliases - \ -# RUN: | FileCheck -check-prefix=CHECK-INST %s +# RUN: | FileCheck -check-prefixes=CHECK-EXPAND,CHECK-INST %s # RUN: llvm-mc -filetype=obj -triple riscv64 < %s \ # RUN: | llvm-objdump -d - \ -# RUN: | FileCheck -check-prefix=CHECK-ALIAS %s +# RUN: | FileCheck -check-prefixes=CHECK-EXPAND,CHECK-ALIAS %s + +# The following check prefixes are used in this test: +# CHECK-INST.....Match the canonical instruction behind the alias. +# CHECK-ALIAS....Match the alias for roundtrip testing. +# CHECK-EXPAND...Match canonical instructions which gets expanded from an alias. # TODO la # TODO lb lh lw @@ -26,7 +31,27 @@ # CHECK-INST: addi zero, zero, 0 # CHECK-ALIAS: nop nop -# TODO li + +# CHECK-INST: addi a0, zero, 0 +# CHECK-ALIAS: mv a0, zero +li x10, 0 +# CHECK-EXPAND: addi a0, zero, 2047 +li x10, 2047 +# CHECK-EXPAND: lui a1, 1 +# CHECK-EXPAND: addi a1, a1, -2048 +li x11, 2048 +# CHECK-EXPAND: lui a1, 1 +# CHECK-EXPAND: addi a1, a1, -1 +li x11, 4095 +# CHECK-EXPAND: lui a2, 1 +li x12, 4096 +# CHECK-EXPAND: lui a2, 1 +# CHECK-EXPAND: addi a2, a2, 1 +li x12, 4097 +# CHECK-EXPAND: lui a2, 524288 +# CHECK-EXPAND: addi a2, a2, -1 +li x12, 2147483647 + # CHECK-INST: addi t6, zero, 0 # CHECK-ALIAS: mv t6, zero mv x31, zero