diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp --- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp +++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp @@ -9,8 +9,10 @@ #include "MCTargetDesc/LoongArchInstPrinter.h" #include "MCTargetDesc/LoongArchMCExpr.h" #include "MCTargetDesc/LoongArchMCTargetDesc.h" +#include "MCTargetDesc/LoongArchMatInt.h" #include "TargetInfo/LoongArchTargetInfo.h" #include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCParsedAsmOperand.h" @@ -69,6 +71,9 @@ bool parseOperand(OperandVector &Operands, StringRef Mnemonic); + // Helper to emit pseudo instruction "li.w/d $rd, $imm". + void emitLoadImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + public: enum LoongArchMatchResultTy { Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY, @@ -350,6 +355,8 @@ IsValidKind; } + bool isImm32() const { return isSImm<32>() || isUImm<32>(); } + /// Gets location of the first token of this operand. SMLoc getStartLoc() const override { return StartLoc; } /// Gets location of the last token of this operand. @@ -659,10 +666,40 @@ return Error(Loc, "unexpected token"); } +void LoongArchAsmParser::emitLoadImm(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + MCRegister DestReg = Inst.getOperand(0).getReg(); + int64_t Imm = Inst.getOperand(1).getImm(); + MCRegister SrcReg = LoongArch::R0; + + if (Inst.getOpcode() == LoongArch::PseudoLI_W) + Imm = SignExtend64<32>(Imm); + + for (LoongArchMatInt::Inst &Inst : LoongArchMatInt::generateInstSeq(Imm)) { + unsigned Opc = Inst.Opc; + if (Opc == LoongArch::LU12I_W) + Out.emitInstruction(MCInstBuilder(Opc).addReg(DestReg).addImm(Inst.Imm), + getSTI()); + else + Out.emitInstruction( + MCInstBuilder(Opc).addReg(DestReg).addReg(SrcReg).addImm(Inst.Imm), + getSTI()); + SrcReg = DestReg; + } +} + bool LoongArchAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, OperandVector &Operands, MCStreamer &Out) { Inst.setLoc(IDLoc); + switch (Inst.getOpcode()) { + default: + break; + case LoongArch::PseudoLI_W: + case LoongArch::PseudoLI_D: + emitLoadImm(Inst, IDLoc, Out); + return false; + } Out.emitInstruction(Inst, getSTI()); return false; } @@ -888,6 +925,10 @@ Operands, ErrorInfo, /*Lower=*/-(1 << 27), /*Upper=*/(1 << 27) - 4, "operand must be a bare symbol name or an immediate must be a multiple " "of 4 in the range"); + case Match_InvalidImm32: { + SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "operand must be a 32 bit immediate"); + } } llvm_unreachable("Unknown match type detected!"); } diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td --- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td +++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td @@ -91,6 +91,9 @@ // A parameterized register class alternative to i32imm/i64imm from Target.td. def grlenimm : Operand; +def imm32 : Operand { + let ParserMatchClass = ImmAsmOperand<"", 32, "">; +} def uimm2 : Operand { let ParserMatchClass = UImmAsmOperand<2>; @@ -1348,6 +1351,15 @@ def : InstAlias<"ret", (JIRL R0, R1, 0)>; def : InstAlias<"jr $rj", (JIRL R0, GPR:$rj, 0)>; +// Load immediate. +let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0, + isAsmParserOnly = 1 in { +def PseudoLI_W : Pseudo<(outs GPR:$rd), (ins imm32:$imm), [], + "li.w", "$rd, $imm">; +def PseudoLI_D : Pseudo<(outs GPR:$rd), (ins grlenimm:$imm), [], + "li.d", "$rd, $imm">, Requires<[IsLA64]>; +} + //===----------------------------------------------------------------------===// // Basic Floating-Point Instructions //===----------------------------------------------------------------------===// diff --git a/llvm/test/MC/LoongArch/Macros/macros-li-bad.s b/llvm/test/MC/LoongArch/Macros/macros-li-bad.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/LoongArch/Macros/macros-li-bad.s @@ -0,0 +1,7 @@ +# RUN: not llvm-mc --triple=loongarch64 %s 2>&1 | FileCheck %s + +li.w $a0, 0x100000000 +# CHECK: :[[#@LINE-1]]:11: error: operand must be a 32 bit immediate + +li.d $a0, 0x10000000000000000 +# CHECK: :[[#@LINE-1]]:11: error: unknown operand diff --git a/llvm/test/MC/LoongArch/Macros/macros-li.s b/llvm/test/MC/LoongArch/Macros/macros-li.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/LoongArch/Macros/macros-li.s @@ -0,0 +1,90 @@ +# RUN: llvm-mc --triple=loongarch64 %s | FileCheck %s + +li.w $a0, 0x0 +# CHECK: ori $a0, $zero, 0 +li.d $a0, 0x0 +# CHECK-NEXT: ori $a0, $zero, 0 + +li.w $a0, 0xfff +# CHECK: ori $a0, $zero, 4095 +li.d $a0, 0xfff +# CHECK-NEXT: ori $a0, $zero, 4095 + +li.w $a0, 0x7ffff000 +# CHECK: lu12i.w $a0, 524287 +li.d $a0, 0x7ffff000 +# CHECK-NEXT: lu12i.w $a0, 524287 + +li.w $a0, 0x80000000 +# CHECK: lu12i.w $a0, -524288 +li.d $a0, 0x80000000 +# CHECK-NEXT: lu12i.w $a0, -524288 +# CHECK-NEXT: lu32i.d $a0, 0 + +li.w $a0, 0xfffff800 +# CHECK: addi.w $a0, $zero, -2048 +li.d $a0, 0xfffff800 +# CHECK-NEXT: addi.w $a0, $zero, -2048 +# CHECK-NEXT: lu32i.d $a0, 0 + +li.w $a0, 0xfffffffffffff800 +# CHECK: addi.w $a0, $zero, -2048 +li.d $a0, 0xfffffffffffff800 +# CHECK-NEXT: addi.w $a0, $zero, -2048 + +li.w $a0, 0xffffffff80000800 +# CHECK: lu12i.w $a0, -524288 +# CHECK-NEXT: ori $a0, $a0, 2048 +li.d $a0, 0xffffffff80000800 +# CHECK-NEXT: lu12i.w $a0, -524288 +# CHECK-NEXT: ori $a0, $a0, 2048 + +li.d $a0, 0x7ffff00000800 +# CHECK: ori $a0, $zero, 2048 +# CHECK-NEXT: lu32i.d $a0, 524287 + +li.d $a0, 0x8000000000fff +# CHECK: ori $a0, $zero, 4095 +# CHECK-NEXT: lu32i.d $a0, -524288 +# CHECK-NEXT: lu52i.d $a0, $a0, 0 + +li.d $a0, 0x8000080000800 +# CHECK: lu12i.w $a0, -524288 +# CHECK-NEXT: ori $a0, $a0, 2048 +# CHECK-NEXT: lu32i.d $a0, -524288 +# CHECK-NEXT: lu52i.d $a0, $a0, 0 + +li.d $a0, 0x80000fffff800 +# CHECK: addi.w $a0, $zero, -2048 +# CHECK-NEXT: lu32i.d $a0, -524288 +# CHECK-NEXT: lu52i.d $a0, $a0, 0 + +li.d $a0, 0xffffffffff000 +# CHECK: lu12i.w $a0, -1 +# CHECK-NEXT: lu52i.d $a0, $a0, 0 + +li.d $a0, 0xffffffffff800 +# CHECK: addi.w $a0, $zero, -2048 +# CHECK-NEXT: lu52i.d $a0, $a0, 0 + +li.d $a0, 0x7ff0000000000000 +# CHECK: lu52i.d $a0, $zero, 2047 + +li.d $a0, 0x7ff0000080000000 +# CHECK: lu12i.w $a0, -524288 +# CHECK-NEXT: lu32i.d $a0, 0 +# CHECK-NEXT: lu52i.d $a0, $a0, 2047 + +li.d $a0, 0x7fffffff800007ff +# CHECK: lu12i.w $a0, -524288 +# CHECK-NEXT: ori $a0, $a0, 2047 +# CHECK-NEXT: lu52i.d $a0, $a0, 2047 + +li.d $a0, 0xfff0000000000fff +# CHECK: ori $a0, $zero, 4095 +# CHECK-NEXT: lu52i.d $a0, $a0, -1 + +li.d $a0, 0xffffffff7ffff800 +# CHECK: lu12i.w $a0, 524287 +# CHECK-NEXT: ori $a0, $a0, 2048 +# CHECK-NEXT: lu32i.d $a0, -1