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 @@ -138,6 +138,7 @@ OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands); OperandMatchResultTy parseBareSymbol(OperandVector &Operands); OperandMatchResultTy parseCallSymbol(OperandVector &Operands); + OperandMatchResultTy parsePseudoJumpSymbol(OperandVector &Operands); OperandMatchResultTy parseJALOffset(OperandVector &Operands); bool parseOperand(OperandVector &Operands, StringRef Mnemonic); @@ -337,6 +338,16 @@ VK == RISCVMCExpr::VK_RISCV_CALL_PLT); } + bool isPseudoJumpSymbol() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + // Must be of 'immediate' type but not a constant. + if (!isImm() || evaluateConstantImm(getImm(), Imm, VK)) + return false; + return RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm) && + VK == RISCVMCExpr::VK_RISCV_CALL; + } + bool isTPRelAddSymbol() const { int64_t Imm; RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; @@ -976,6 +987,10 @@ SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error(ErrorLoc, "operand must be a bare symbol name"); } + case Match_InvalidPseudoJumpSymbol: { + SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "operand must be a valid jump target"); + } case Match_InvalidCallSymbol: { SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error(ErrorLoc, "operand must be a bare symbol name"); @@ -1287,6 +1302,27 @@ return MatchOperand_Success; } +OperandMatchResultTy +RISCVAsmParser::parsePseudoJumpSymbol(OperandVector &Operands) { + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + const MCExpr *Res; + + if (getParser().parseExpression(Res)) + return MatchOperand_ParseFail; + + if (Res->getKind() != MCExpr::ExprKind::SymbolRef || + cast(Res)->getKind() == + MCSymbolRefExpr::VariantKind::VK_PLT) { + Error(S, "operand must be a valid jump target"); + return MatchOperand_ParseFail; + } + + Res = RISCVMCExpr::create(Res, RISCVMCExpr::VK_RISCV_CALL, getContext()); + Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64())); + return MatchOperand_Success; +} + OperandMatchResultTy RISCVAsmParser::parseJALOffset(OperandVector &Operands) { // Parsing jal operands is fiddly due to the `jal foo` and `jal ra, foo` // both being acceptable forms. When parsing `jal ra, foo` this function diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -89,9 +89,9 @@ return new RISCVMCCodeEmitter(Ctx, MCII); } -// Expand PseudoCALL(Reg) and PseudoTAIL to AUIPC and JALR with relocation -// types. We expand PseudoCALL(Reg) and PseudoTAIL while encoding, meaning AUIPC -// and JALR won't go through RISCV MC to MC compressed instruction +// Expand PseudoCALL(Reg), PseudoTAIL and PseudoJump to AUIPC and JALR with +// relocation types. We those pseudo-instructions while encoding them, meaning +// AUIPC and JALR won't go through RISCV MC to MC compressed instruction // transformation. This is acceptable because AUIPC has no 16-bit form and // C_JALR have no immediate operand field. We let linker relaxation deal with // it. When linker relaxation enabled, AUIPC and JALR have chance relax to JAL. @@ -108,9 +108,12 @@ } else if (MI.getOpcode() == RISCV::PseudoCALLReg) { Func = MI.getOperand(1); Ra = MI.getOperand(0).getReg(); - } else { + } else if (MI.getOpcode() == RISCV::PseudoCALL) { Func = MI.getOperand(0); Ra = RISCV::X1; + } else if (MI.getOpcode() == RISCV::PseudoJump) { + Func = MI.getOperand(1); + Ra = MI.getOperand(0).getReg(); } uint32_t Binary; @@ -125,8 +128,9 @@ Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); support::endian::write(OS, Binary, support::little); - if (MI.getOpcode() == RISCV::PseudoTAIL) - // Emit JALR X0, X6, 0 + if (MI.getOpcode() == RISCV::PseudoTAIL || + MI.getOpcode() == RISCV::PseudoJump) + // Emit JALR X0, Ra, 0 TmpInst = MCInstBuilder(RISCV::JALR).addReg(RISCV::X0).addReg(Ra).addImm(0); else // Emit JALR Ra, Ra, 0 @@ -182,7 +186,8 @@ if (MI.getOpcode() == RISCV::PseudoCALLReg || MI.getOpcode() == RISCV::PseudoCALL || - MI.getOpcode() == RISCV::PseudoTAIL) { + MI.getOpcode() == RISCV::PseudoTAIL || + MI.getOpcode() == RISCV::PseudoJump) { expandFunctionCall(MI, OS, Fixups, STI); MCNumEmitted += 2; return; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -473,6 +473,7 @@ return 0; case RISCV::PseudoCALLReg: case RISCV::PseudoCALL: + case RISCV::PseudoJump: case RISCV::PseudoTAIL: case RISCV::PseudoLLA: case RISCV::PseudoLA: diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -222,6 +222,18 @@ let ParserMatchClass = CallSymbol; } +def PseudoJumpSymbol : AsmOperandClass { + let Name = "PseudoJumpSymbol"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidPseudoJumpSymbol"; + let ParserMethod = "parsePseudoJumpSymbol"; +} + +// A bare symbol used for pseudo jumps only. +def pseudo_jump_symbol : Operand { + let ParserMatchClass = PseudoJumpSymbol; +} + def TPRelAddSymbol : AsmOperandClass { let Name = "TPRelAddSymbol"; let RenderMethod = "addImmOperands"; @@ -968,6 +980,12 @@ def : Pat<(riscv_tail (iPTR texternalsym:$dst)), (PseudoTAIL texternalsym:$dst)>; +let isCall = 0, isBarrier = 0, isCodeGenOnly = 0, hasSideEffects = 0, + mayStore = 0, mayLoad = 0 in +def PseudoJump : Pseudo<(outs GPR:$rd), (ins pseudo_jump_symbol:$target), []> { + let AsmString = "jump\t$target, $rd"; +} + let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0, isAsmParserOnly = 1 in def PseudoLLA : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [], diff --git a/llvm/test/MC/RISCV/pseudo-jump-invalid.s b/llvm/test/MC/RISCV/pseudo-jump-invalid.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/RISCV/pseudo-jump-invalid.s @@ -0,0 +1,5 @@ +# RUN: not llvm-mc -triple riscv32 < %s 2>&1 | FileCheck %s + +jump 1234, x31 # CHECK: :[[@LINE]]:6: error: operand must be a valid jump target +jump foo@plt, x31 # CHECK: :[[@LINE]]:6: error: operand must be a valid jump target +jump %pcrel_lo(1234), x31 # CHECK: :[[@LINE]]:6: error: unknown token in expression diff --git a/llvm/test/MC/RISCV/pseudo-jump.s b/llvm/test/MC/RISCV/pseudo-jump.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/RISCV/pseudo-jump.s @@ -0,0 +1,28 @@ +# RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ +# RUN: | llvm-objdump -d - | FileCheck -check-prefix=INSTR %s +# RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=RELOC %s +# RUN: llvm-mc -triple riscv32 < %s -show-encoding \ +# RUN: | FileCheck -check-prefix=FIXUP %s + +.long foo + +jump foo, x31 +# RELOC: R_RISCV_CALL foo 0x0 +# INSTR: auipc t6, 0 +# INSTR: jr t6 +# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_call + +# Ensure that jumps to symbols whose names coincide with register names work. + +jump zero, x1 +# RELOC: R_RISCV_CALL zero 0x0 +# INSTR: auipc ra, 0 +# INSTR: ret +# FIXUP: fixup A - offset: 0, value: zero, kind: fixup_riscv_call + +1: +jump 1b, x31 +# INSTR: auipc t6, 0 +# INSTR: jr t6 +# FIXUP: fixup A - offset: 0, value: .Ltmp0, kind: fixup_riscv_call