diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp --- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp +++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -13,6 +13,7 @@ #include "MCTargetDesc/RISCVInstPrinter.h" #include "MCTargetDesc/RISCVMCExpr.h" +#include "MCTargetDesc/RISCVMatInt.h" #include "MCTargetDesc/RISCVTargetStreamer.h" #include "RISCV.h" #include "RISCVTargetMachine.h" @@ -129,8 +130,62 @@ PatchPointOpers Opers(&MI); + const MachineOperand &CalleeMO = Opers.getCallTarget(); unsigned EncodedBytes = 0; + if (CalleeMO.isImm()) { + uint64_t CallTarget = CalleeMO.getImm(); + if (CallTarget) { + assert((CallTarget & 0xFFFF'FFFF'FFFF) == CallTarget && + "High 16 bits of call target should be zero."); + // Materialize the jump address: + + RISCVMatInt::InstSeq Seq = + RISCVMatInt::generateInstSeq(CallTarget, STI->getFeatureBits()); + assert(!Seq.empty()); + + Register SrcReg = RISCV::X0; + Register DstReg = RISCV::X1; + for (RISCVMatInt::Inst &Inst : Seq) { + if (Inst.Opc == RISCV::LUI) { + EmitToStreamer( + OutStreamer, + MCInstBuilder(RISCV::LUI).addReg(DstReg).addImm(Inst.Imm)); + } else if (Inst.Opc == RISCV::ADD_UW) { + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::ADD_UW) + .addReg(DstReg) + .addReg(SrcReg) + .addReg(RISCV::X0)); + } else if (Inst.Opc == RISCV::SH1ADD || Inst.Opc == RISCV::SH2ADD || + Inst.Opc == RISCV::SH3ADD) { + EmitToStreamer( + OutStreamer, + MCInstBuilder(Inst.Opc).addReg(DstReg).addReg(SrcReg).addReg( + SrcReg)); + } else { + EmitToStreamer( + OutStreamer, + MCInstBuilder(Inst.Opc).addReg(DstReg).addReg(SrcReg).addImm( + Inst.Imm)); + } + EncodedBytes += 4; + // Only the first instruction has X0 as its source. + SrcReg = DstReg; + } + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JALR) + .addReg(RISCV::X1) + .addReg(RISCV::X1) + .addImm(0)); + EncodedBytes += 4; + } + } else if (CalleeMO.isGlobal()) { + MCOperand CallTargetMCOp; + llvm::lowerRISCVMachineOperandToMCOperand(CalleeMO, CallTargetMCOp, *this); + EmitToStreamer(OutStreamer, + MCInstBuilder(RISCV::PseudoCALL).addOperand(CallTargetMCOp)); + EncodedBytes += 4; + } + // Emit padding. unsigned NumBytes = Opers.getNumPatchBytes(); assert(NumBytes >= EncodedBytes && @@ -146,6 +201,36 @@ if (unsigned PatchBytes = SOpers.getNumPatchBytes()) { assert(PatchBytes % 4 == 0 && "Invalid number of NOP bytes requested!"); emitNops(PatchBytes / 4); + } else { + // Lower call target and choose correct opcode + const MachineOperand &CallTarget = SOpers.getCallTarget(); + MCOperand CallTargetMCOp; + switch (CallTarget.getType()) { + case MachineOperand::MO_GlobalAddress: + case MachineOperand::MO_ExternalSymbol: + llvm::lowerRISCVMachineOperandToMCOperand(CallTarget, CallTargetMCOp, + *this); + EmitToStreamer( + OutStreamer, + MCInstBuilder(RISCV::PseudoCALL).addOperand(CallTargetMCOp)); + break; + case MachineOperand::MO_Immediate: + CallTargetMCOp = MCOperand::createImm(CallTarget.getImm()); + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JAL) + .addReg(RISCV::X1) + .addOperand(CallTargetMCOp)); + break; + case MachineOperand::MO_Register: + CallTargetMCOp = MCOperand::createReg(CallTarget.getReg()); + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JALR) + .addReg(RISCV::X1) + .addOperand(CallTargetMCOp) + .addImm(0)); + break; + default: + llvm_unreachable("Unsupported operand type in statepoint call target"); + break; + } } auto &Ctx = OutStreamer.getContext(); diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -10118,6 +10118,12 @@ case RISCV::PseudoQuietFLT_D: return emitQuietFCMP(MI, BB, RISCV::FLT_D, RISCV::FEQ_D, Subtarget); case TargetOpcode::STATEPOINT: + // STATEPOINT is a pseudo instruction which has no implicit defs/uses + // while jal call instruction (where statepoint will be lowered at the end) + // has implicit def. Add this implicit dead def here as a workaround. + MI.addOperand(*MI.getMF(), MachineOperand::CreateReg(RISCV::X1, true, true, + false, true)); + LLVM_FALLTHROUGH; case TargetOpcode::STACKMAP: case TargetOpcode::PATCHPOINT: if (!Subtarget.is64Bit()) 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 @@ -995,6 +995,8 @@ return 2; } + unsigned NumBytes = 0; + switch (Opcode) { case TargetOpcode::STACKMAP: // The upper bound for a stackmap intrinsic is the full length of its shadow @@ -1004,7 +1006,11 @@ return PatchPointOpers(&MI).getNumPatchBytes(); case TargetOpcode::STATEPOINT: // The size of the statepoint intrinsic is the number of bytes requested - return StatepointOpers(&MI).getNumPatchBytes(); + NumBytes = StatepointOpers(&MI).getNumPatchBytes(); + // A statepoint is at least a PSEUDOCALL + if (NumBytes < 8) + NumBytes = 8; + return NumBytes; default: return get(Opcode).getSize(); } diff --git a/llvm/test/CodeGen/RISCV/rv64-patchpoint.ll b/llvm/test/CodeGen/RISCV/rv64-patchpoint.ll --- a/llvm/test/CodeGen/RISCV/rv64-patchpoint.ll +++ b/llvm/test/CodeGen/RISCV/rv64-patchpoint.ll @@ -1,6 +1,55 @@ ; RUN: llc -mtriple=riscv64 -debug-entry-values -enable-misched=0 < %s | FileCheck %s ; RUN: llc -mtriple=riscv64 -debug-entry-values -enable-misched=0 -fast-isel -fast-isel-abort=1 < %s | FileCheck %s +; Trivial patchpoint codegen +; +define i64 @trivial_patchpoint_codegen(i64 %p1, i64 %p2, i64 %p3, i64 %p4) { +entry: + +; CHECK-LABEL: .Ltmp0: +; CHECK-NEXT: lui ra, 3563 +; CHECK-NEXT: addiw ra, ra, -577 +; CHECK-NEXT: slli ra, ra, 12 +; CHECK-NEXT: addi ra, ra, -259 +; CHECK-NEXT: slli ra, ra, 12 +; CHECK-NEXT: addi ra, ra, -1282 +; CHECK-NEXT: jalr ra + +; CHECK-LABEL: .Ltmp1: +; CHECK-NEXT: lui ra, 3563 +; CHECK-NEXT: addiw ra, ra, -577 +; CHECK-NEXT: slli ra, ra, 12 +; CHECK-NEXT: addi ra, ra, -259 +; CHECK-NEXT: slli ra, ra, 12 +; CHECK-NEXT: addi ra, ra, -1281 +; CHECK-NEXT: jalr ra +; CHECK: ret + %resolveCall2 = inttoptr i64 244837814094590 to i8* + %result = tail call i64 (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.i64(i64 2, i32 28, i8* %resolveCall2, i32 4, i64 %p1, i64 %p2, i64 %p3, i64 %p4) + %resolveCall3 = inttoptr i64 244837814094591 to i8* + tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 3, i32 28, i8* %resolveCall3, i32 2, i64 %p1, i64 %result) + ret i64 %result +} + +; Test patchpoints reusing the same TargetConstant. +; There is no way to verify this, since it depends on memory allocation. +; But I think it's useful to include as a working example. +define i64 @testLowerConstant(i64 %arg, i64 %tmp2, i64 %tmp10, i64* %tmp33, i64 %tmp79) { +entry: + %tmp80 = add i64 %tmp79, -16 + %tmp81 = inttoptr i64 %tmp80 to i64* + %tmp82 = load i64, i64* %tmp81, align 8 + tail call void (i64, i32, ...) @llvm.experimental.stackmap(i64 14, i32 8, i64 %arg, i64 %tmp2, i64 %tmp10, i64 %tmp82) + tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 15, i32 32, i8* null, i32 3, i64 %arg, i64 %tmp10, i64 %tmp82) + %tmp83 = load i64, i64* %tmp33, align 8 + %tmp84 = add i64 %tmp83, -24 + %tmp85 = inttoptr i64 %tmp84 to i64* + %tmp86 = load i64, i64* %tmp85, align 8 + tail call void (i64, i32, ...) @llvm.experimental.stackmap(i64 17, i32 8, i64 %arg, i64 %tmp10, i64 %tmp86) + tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 18, i32 32, i8* null, i32 3, i64 %arg, i64 %tmp10, i64 %tmp86) + ret i64 10 +} + ; Test small patchpoints that don't emit calls. define void @small_patchpoint_codegen(i64 %p1, i64 %p2, i64 %p3, i64 %p4) { entry: