Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RISCV] Implement Statepoint and Patchpoint lowering to call instructions #77337

Merged
merged 6 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "MCTargetDesc/RISCVBaseInfo.h"
#include "MCTargetDesc/RISCVInstPrinter.h"
#include "MCTargetDesc/RISCVMCExpr.h"
#include "MCTargetDesc/RISCVMatInt.h"
#include "MCTargetDesc/RISCVTargetStreamer.h"
#include "RISCV.h"
#include "RISCVMachineFunctionInfo.h"
Expand Down Expand Up @@ -153,8 +154,35 @@ void RISCVAsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM,

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:
SmallVector<MCInst, 8> Seq;
RISCVMatInt::generateMCInstSeq(CallTarget, *STI, RISCV::X1, Seq);
for (MCInst &Inst : Seq) {
topperc marked this conversation as resolved.
Show resolved Hide resolved
bool Compressed = EmitToStreamer(OutStreamer, Inst);
EncodedBytes += Compressed ? 2 : 4;
}
bool Compressed = EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JALR)
.addReg(RISCV::X1)
.addReg(RISCV::X1)
.addImm(0));
EncodedBytes += Compressed ? 2 : 4;
}
} else if (CalleeMO.isGlobal()) {
MCOperand CallTargetMCOp;
lowerOperand(CalleeMO, CallTargetMCOp);
EmitToStreamer(OutStreamer,
MCInstBuilder(RISCV::PseudoCALL).addOperand(CallTargetMCOp));
EncodedBytes += 8;
}

// Emit padding.
unsigned NumBytes = Opers.getNumPatchBytes();
assert(NumBytes >= EncodedBytes &&
Expand All @@ -173,6 +201,35 @@ void RISCVAsmPrinter::LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM,
assert(PatchBytes % NOPBytes == 0 &&
"Invalid number of NOP bytes requested!");
emitNops(PatchBytes / NOPBytes);
} else {
// Lower call target and choose correct opcode
const MachineOperand &CallTarget = SOpers.getCallTarget();
MCOperand CallTargetMCOp;
switch (CallTarget.getType()) {
wangpc-pp marked this conversation as resolved.
Show resolved Hide resolved
case MachineOperand::MO_GlobalAddress:
case MachineOperand::MO_ExternalSymbol:
lowerOperand(CallTarget, CallTargetMCOp);
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();
Expand Down
11 changes: 11 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17902,6 +17902,17 @@ RISCVTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
case RISCV::PseudoFROUND_D_IN32X:
return emitFROUND(MI, BB, 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. This def is early-clobber as it will be set at
// the moment of the call and earlier than any use is read.
// Add this implicit dead def here as a workaround.
MI.addOperand(*MI.getMF(),
MachineOperand::CreateReg(
RISCV::X1, /*isDef*/ true,
/*isImp*/ true, /*isKill*/ false, /*isDead*/ true,
/*isUndef*/ false, /*isEarlyClobber*/ true));
[[fallthrough]];
case TargetOpcode::STACKMAP:
case TargetOpcode::PATCHPOINT:
if (!Subtarget.is64Bit())
Expand Down
7 changes: 5 additions & 2 deletions llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1469,9 +1469,12 @@ unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
case TargetOpcode::PATCHPOINT:
wangpc-pp marked this conversation as resolved.
Show resolved Hide resolved
// The size of the patchpoint intrinsic is the number of bytes requested
return PatchPointOpers(&MI).getNumPatchBytes();
case TargetOpcode::STATEPOINT:
case TargetOpcode::STATEPOINT: {
// The size of the statepoint intrinsic is the number of bytes requested
return StatepointOpers(&MI).getNumPatchBytes();
unsigned NumBytes = StatepointOpers(&MI).getNumPatchBytes();
// No patch bytes means at most a PseudoCall is emitted
return std::max(NumBytes, 8U);
}
default:
return get(Opcode).getSize();
}
Expand Down
46 changes: 45 additions & 1 deletion llvm/test/CodeGen/RISCV/rv64-patchpoint.ll
Original file line number Diff line number Diff line change
@@ -1,12 +1,56 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv64 -debug-entry-values -enable-misched=0 < %s | FileCheck %s

; Trivial patchpoint codegen
;
define i64 @trivial_patchpoint_codegen(i64 %p1, i64 %p2, i64 %p3, i64 %p4) {
; CHECK-LABEL: trivial_patchpoint_codegen:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: addi sp, sp, -16
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
; CHECK-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
; CHECK-NEXT: .cfi_offset s0, -8
; CHECK-NEXT: .cfi_offset s1, -16
; CHECK-NEXT: mv s0, a0
; CHECK-NEXT: .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-NEXT: mv s1, a0
; CHECK-NEXT: mv a0, s0
; CHECK-NEXT: mv a1, s1
; CHECK-NEXT: .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-NEXT: mv a0, s1
; CHECK-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
; CHECK-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
; CHECK-NEXT: addi sp, sp, 16
; CHECK-NEXT: ret
entry:
%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 small patchpoints that don't emit calls.
define void @small_patchpoint_codegen(i64 %p1, i64 %p2, i64 %p3, i64 %p4) {
; CHECK-LABEL: small_patchpoint_codegen:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: .cfi_def_cfa_offset 0
; CHECK-NEXT: .Ltmp0:
; CHECK-NEXT: .Ltmp2:
; CHECK-NEXT: nop
; CHECK-NEXT: nop
; CHECK-NEXT: nop
Expand Down
16 changes: 16 additions & 0 deletions llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering-x1.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
; RUN: llc -mtriple riscv64 -verify-machineinstrs -stop-after=prologepilog < %s | FileCheck %s

; Check that STATEPOINT instruction has an early clobber implicit def for LR.
target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "riscv64"

define void @test() "frame-pointer"="all" gc "statepoint-example" {
entry:
%safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void ()) @return_i1, i32 0, i32 0, i32 0, i32 0) ["gc-live" ()]
; CHECK: STATEPOINT 0, 0, 0, target-flags(riscv-call) @return_i1, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, csr_ilp32_lp64, implicit-def $x2, implicit-def dead early-clobber $x1
ret void
}


declare void @return_i1()
declare token @llvm.experimental.gc.statepoint.p0(i64, i32, ptr, i32, i32, ...)
23 changes: 23 additions & 0 deletions llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering-x2.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
; RUN: llc -mtriple riscv64 -verify-machineinstrs -stop-after=prologepilog < %s | FileCheck %s

; Check that STATEPOINT instruction prefer to use x2 in presense of x8.
target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "riscv64"

declare void @consume(ptr addrspace(1) %obj)

define i1 @test(ptr addrspace(1) %a) "frame-pointer"="all" gc "statepoint-example" {
entry:
%safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(i1 ()) @return_i1, i32 0, i32 0, i32 0, i32 0) ["gc-live" (ptr addrspace(1) %a)]
; CHECK: STATEPOINT 0, 0, 0, target-flags(riscv-call) @return_i1, 2, 0, 2, 0, 2, 0, 2, 1, 1, 8, $x8, -32, 2, 0, 2, 1, 0, 0
%call1 = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token, i32 0, i32 0)
%call2 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token)
call void @consume(ptr addrspace(1) %call1)
ret i1 %call2
}


declare i1 @return_i1()
declare token @llvm.experimental.gc.statepoint.p0(i64, i32, ptr, i32, i32, ...)
declare ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token, i32, i32)
declare i1 @llvm.experimental.gc.result.i1(token)
Loading
Loading