Skip to content

Commit 53003e3

Browse files
authored
[RISCV] Implement Statepoint and Patchpoint lowering to call instructions (#77337)
This patch adds stackmap support for RISC-V with call targets. Based on patch from https://reviews.llvm.org/D129848.
1 parent 026165f commit 53003e3

File tree

7 files changed

+419
-3
lines changed

7 files changed

+419
-3
lines changed

llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "MCTargetDesc/RISCVBaseInfo.h"
1515
#include "MCTargetDesc/RISCVInstPrinter.h"
1616
#include "MCTargetDesc/RISCVMCExpr.h"
17+
#include "MCTargetDesc/RISCVMatInt.h"
1718
#include "MCTargetDesc/RISCVTargetStreamer.h"
1819
#include "RISCV.h"
1920
#include "RISCVMachineFunctionInfo.h"
@@ -153,8 +154,35 @@ void RISCVAsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM,
153154

154155
PatchPointOpers Opers(&MI);
155156

157+
const MachineOperand &CalleeMO = Opers.getCallTarget();
156158
unsigned EncodedBytes = 0;
157159

160+
if (CalleeMO.isImm()) {
161+
uint64_t CallTarget = CalleeMO.getImm();
162+
if (CallTarget) {
163+
assert((CallTarget & 0xFFFF'FFFF'FFFF) == CallTarget &&
164+
"High 16 bits of call target should be zero.");
165+
// Materialize the jump address:
166+
SmallVector<MCInst, 8> Seq;
167+
RISCVMatInt::generateMCInstSeq(CallTarget, *STI, RISCV::X1, Seq);
168+
for (MCInst &Inst : Seq) {
169+
bool Compressed = EmitToStreamer(OutStreamer, Inst);
170+
EncodedBytes += Compressed ? 2 : 4;
171+
}
172+
bool Compressed = EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JALR)
173+
.addReg(RISCV::X1)
174+
.addReg(RISCV::X1)
175+
.addImm(0));
176+
EncodedBytes += Compressed ? 2 : 4;
177+
}
178+
} else if (CalleeMO.isGlobal()) {
179+
MCOperand CallTargetMCOp;
180+
lowerOperand(CalleeMO, CallTargetMCOp);
181+
EmitToStreamer(OutStreamer,
182+
MCInstBuilder(RISCV::PseudoCALL).addOperand(CallTargetMCOp));
183+
EncodedBytes += 8;
184+
}
185+
158186
// Emit padding.
159187
unsigned NumBytes = Opers.getNumPatchBytes();
160188
assert(NumBytes >= EncodedBytes &&
@@ -173,6 +201,35 @@ void RISCVAsmPrinter::LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM,
173201
assert(PatchBytes % NOPBytes == 0 &&
174202
"Invalid number of NOP bytes requested!");
175203
emitNops(PatchBytes / NOPBytes);
204+
} else {
205+
// Lower call target and choose correct opcode
206+
const MachineOperand &CallTarget = SOpers.getCallTarget();
207+
MCOperand CallTargetMCOp;
208+
switch (CallTarget.getType()) {
209+
case MachineOperand::MO_GlobalAddress:
210+
case MachineOperand::MO_ExternalSymbol:
211+
lowerOperand(CallTarget, CallTargetMCOp);
212+
EmitToStreamer(
213+
OutStreamer,
214+
MCInstBuilder(RISCV::PseudoCALL).addOperand(CallTargetMCOp));
215+
break;
216+
case MachineOperand::MO_Immediate:
217+
CallTargetMCOp = MCOperand::createImm(CallTarget.getImm());
218+
EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JAL)
219+
.addReg(RISCV::X1)
220+
.addOperand(CallTargetMCOp));
221+
break;
222+
case MachineOperand::MO_Register:
223+
CallTargetMCOp = MCOperand::createReg(CallTarget.getReg());
224+
EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JALR)
225+
.addReg(RISCV::X1)
226+
.addOperand(CallTargetMCOp)
227+
.addImm(0));
228+
break;
229+
default:
230+
llvm_unreachable("Unsupported operand type in statepoint call target");
231+
break;
232+
}
176233
}
177234

178235
auto &Ctx = OutStreamer.getContext();

llvm/lib/Target/RISCV/RISCVISelLowering.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17910,6 +17910,17 @@ RISCVTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
1791017910
case RISCV::PseudoFROUND_D_IN32X:
1791117911
return emitFROUND(MI, BB, Subtarget);
1791217912
case TargetOpcode::STATEPOINT:
17913+
// STATEPOINT is a pseudo instruction which has no implicit defs/uses
17914+
// while jal call instruction (where statepoint will be lowered at the end)
17915+
// has implicit def. This def is early-clobber as it will be set at
17916+
// the moment of the call and earlier than any use is read.
17917+
// Add this implicit dead def here as a workaround.
17918+
MI.addOperand(*MI.getMF(),
17919+
MachineOperand::CreateReg(
17920+
RISCV::X1, /*isDef*/ true,
17921+
/*isImp*/ true, /*isKill*/ false, /*isDead*/ true,
17922+
/*isUndef*/ false, /*isEarlyClobber*/ true));
17923+
[[fallthrough]];
1791317924
case TargetOpcode::STACKMAP:
1791417925
case TargetOpcode::PATCHPOINT:
1791517926
if (!Subtarget.is64Bit())

llvm/lib/Target/RISCV/RISCVInstrInfo.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,9 +1469,12 @@ unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
14691469
case TargetOpcode::PATCHPOINT:
14701470
// The size of the patchpoint intrinsic is the number of bytes requested
14711471
return PatchPointOpers(&MI).getNumPatchBytes();
1472-
case TargetOpcode::STATEPOINT:
1472+
case TargetOpcode::STATEPOINT: {
14731473
// The size of the statepoint intrinsic is the number of bytes requested
1474-
return StatepointOpers(&MI).getNumPatchBytes();
1474+
unsigned NumBytes = StatepointOpers(&MI).getNumPatchBytes();
1475+
// No patch bytes means at most a PseudoCall is emitted
1476+
return std::max(NumBytes, 8U);
1477+
}
14751478
default:
14761479
return get(Opcode).getSize();
14771480
}

llvm/test/CodeGen/RISCV/rv64-patchpoint.ll

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,56 @@
11
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
22
; RUN: llc -mtriple=riscv64 -debug-entry-values -enable-misched=0 < %s | FileCheck %s
33

4+
; Trivial patchpoint codegen
5+
;
6+
define i64 @trivial_patchpoint_codegen(i64 %p1, i64 %p2, i64 %p3, i64 %p4) {
7+
; CHECK-LABEL: trivial_patchpoint_codegen:
8+
; CHECK: # %bb.0: # %entry
9+
; CHECK-NEXT: addi sp, sp, -16
10+
; CHECK-NEXT: .cfi_def_cfa_offset 16
11+
; CHECK-NEXT: sd s0, 8(sp) # 8-byte Folded Spill
12+
; CHECK-NEXT: sd s1, 0(sp) # 8-byte Folded Spill
13+
; CHECK-NEXT: .cfi_offset s0, -8
14+
; CHECK-NEXT: .cfi_offset s1, -16
15+
; CHECK-NEXT: mv s0, a0
16+
; CHECK-NEXT: .Ltmp0:
17+
; CHECK-NEXT: lui ra, 3563
18+
; CHECK-NEXT: addiw ra, ra, -577
19+
; CHECK-NEXT: slli ra, ra, 12
20+
; CHECK-NEXT: addi ra, ra, -259
21+
; CHECK-NEXT: slli ra, ra, 12
22+
; CHECK-NEXT: addi ra, ra, -1282
23+
; CHECK-NEXT: jalr ra
24+
; CHECK-NEXT: mv s1, a0
25+
; CHECK-NEXT: mv a0, s0
26+
; CHECK-NEXT: mv a1, s1
27+
; CHECK-NEXT: .Ltmp1:
28+
; CHECK-NEXT: lui ra, 3563
29+
; CHECK-NEXT: addiw ra, ra, -577
30+
; CHECK-NEXT: slli ra, ra, 12
31+
; CHECK-NEXT: addi ra, ra, -259
32+
; CHECK-NEXT: slli ra, ra, 12
33+
; CHECK-NEXT: addi ra, ra, -1281
34+
; CHECK-NEXT: jalr ra
35+
; CHECK-NEXT: mv a0, s1
36+
; CHECK-NEXT: ld s0, 8(sp) # 8-byte Folded Reload
37+
; CHECK-NEXT: ld s1, 0(sp) # 8-byte Folded Reload
38+
; CHECK-NEXT: addi sp, sp, 16
39+
; CHECK-NEXT: ret
40+
entry:
41+
%resolveCall2 = inttoptr i64 244837814094590 to i8*
42+
%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)
43+
%resolveCall3 = inttoptr i64 244837814094591 to i8*
44+
tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 3, i32 28, i8* %resolveCall3, i32 2, i64 %p1, i64 %result)
45+
ret i64 %result
46+
}
47+
448
; Test small patchpoints that don't emit calls.
549
define void @small_patchpoint_codegen(i64 %p1, i64 %p2, i64 %p3, i64 %p4) {
650
; CHECK-LABEL: small_patchpoint_codegen:
751
; CHECK: # %bb.0: # %entry
852
; CHECK-NEXT: .cfi_def_cfa_offset 0
9-
; CHECK-NEXT: .Ltmp0:
53+
; CHECK-NEXT: .Ltmp2:
1054
; CHECK-NEXT: nop
1155
; CHECK-NEXT: nop
1256
; CHECK-NEXT: nop
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
; RUN: llc -mtriple riscv64 -verify-machineinstrs -stop-after=prologepilog < %s | FileCheck %s
2+
3+
; Check that STATEPOINT instruction has an early clobber implicit def for LR.
4+
target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128"
5+
target triple = "riscv64"
6+
7+
define void @test() "frame-pointer"="all" gc "statepoint-example" {
8+
entry:
9+
%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" ()]
10+
; 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
11+
ret void
12+
}
13+
14+
15+
declare void @return_i1()
16+
declare token @llvm.experimental.gc.statepoint.p0(i64, i32, ptr, i32, i32, ...)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
; RUN: llc -mtriple riscv64 -verify-machineinstrs -stop-after=prologepilog < %s | FileCheck %s
2+
3+
; Check that STATEPOINT instruction prefer to use x2 in presense of x8.
4+
target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128"
5+
target triple = "riscv64"
6+
7+
declare void @consume(ptr addrspace(1) %obj)
8+
9+
define i1 @test(ptr addrspace(1) %a) "frame-pointer"="all" gc "statepoint-example" {
10+
entry:
11+
%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)]
12+
; 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
13+
%call1 = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token, i32 0, i32 0)
14+
%call2 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token)
15+
call void @consume(ptr addrspace(1) %call1)
16+
ret i1 %call2
17+
}
18+
19+
20+
declare i1 @return_i1()
21+
declare token @llvm.experimental.gc.statepoint.p0(i64, i32, ptr, i32, i32, ...)
22+
declare ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token, i32, i32)
23+
declare i1 @llvm.experimental.gc.result.i1(token)

0 commit comments

Comments
 (0)