From b4227170f0c30e65c4eb7481977f3d1e51e43e61 Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Mon, 22 May 2023 21:26:37 +0200 Subject: [PATCH 1/6] [RISCV] Add Stackmap/Statepoint/Patchpoint support with targets --- llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp | 57 +++++++++++++++++++++ llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 11 ++++ llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 9 +++- llvm/test/CodeGen/RISCV/rv64-patchpoint.ll | 46 ++++++++++++++++- 4 files changed, 120 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp index 9982a73ee914d9..0061a5c17caa0b 100644 --- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp +++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -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" @@ -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 Seq; + RISCVMatInt::generateMCInstSeq(CallTarget, *STI, RISCV::X1, Seq); + for (MCInst &Inst : Seq) { + EmitToStreamer(OutStreamer, Inst); + } + EncodedBytes += Seq.size() * 4; + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JALR) + .addReg(RISCV::X1) + .addReg(RISCV::X1) + .addImm(0)); + EncodedBytes += 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 && @@ -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()) { + 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(); diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 6e97575c167cd5..55246247f19904 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -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()) diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index be63bc936ae8a1..d1981fe8f7445d 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -1469,9 +1469,14 @@ unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { case TargetOpcode::PATCHPOINT: // 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(); + // 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 index 51c2ae908e8425..adf5f9863b79f8 100644 --- a/llvm/test/CodeGen/RISCV/rv64-patchpoint.ll +++ b/llvm/test/CodeGen/RISCV/rv64-patchpoint.ll @@ -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 From 2b60af9fbf27b780747260d5c82d8fdce7cecb52 Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Mon, 12 Feb 2024 19:55:58 +0100 Subject: [PATCH 2/6] Small fixes --- llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp | 3 +-- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp index 0061a5c17caa0b..6a727f250521b7 100644 --- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp +++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -165,9 +165,8 @@ void RISCVAsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM, // Materialize the jump address: SmallVector Seq; RISCVMatInt::generateMCInstSeq(CallTarget, *STI, RISCV::X1, Seq); - for (MCInst &Inst : Seq) { + for (MCInst &Inst : Seq) EmitToStreamer(OutStreamer, Inst); - } EncodedBytes += Seq.size() * 4; EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JALR) .addReg(RISCV::X1) diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index d1981fe8f7445d..5837a74d3f6a67 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -1473,9 +1473,7 @@ unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { // The size of the statepoint intrinsic is the number of bytes requested unsigned NumBytes = StatepointOpers(&MI).getNumPatchBytes(); // A statepoint is at least a PseudoCALL - if (NumBytes < 8) - NumBytes = 8; - return NumBytes; + return std::max(NumBytes, 8U); } default: return get(Opcode).getSize(); From 8314ae2b0c0f7ecc35df54ecfd96f19376fb9cb1 Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Tue, 2 Apr 2024 13:41:08 +0200 Subject: [PATCH 3/6] Add statepoint tests --- .../RISCV/rv64-statepoint-call-lowering-x1.ll | 16 ++ .../RISCV/rv64-statepoint-call-lowering-x2.ll | 23 ++ .../RISCV/rv64-statepoint-call-lowering.ll | 262 ++++++++++++++++++ 3 files changed, 301 insertions(+) create mode 100644 llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering-x1.ll create mode 100644 llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering-x2.ll create mode 100644 llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering.ll diff --git a/llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering-x1.ll b/llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering-x1.ll new file mode 100644 index 00000000000000..3ba49653cd01ea --- /dev/null +++ b/llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering-x1.ll @@ -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, ...) diff --git a/llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering-x2.ll b/llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering-x2.ll new file mode 100644 index 00000000000000..9c99f64bcacc0d --- /dev/null +++ b/llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering-x2.ll @@ -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) diff --git a/llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering.ll b/llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering.ll new file mode 100644 index 00000000000000..2fa344d4d79a7f --- /dev/null +++ b/llvm/test/CodeGen/RISCV/rv64-statepoint-call-lowering.ll @@ -0,0 +1,262 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -verify-machineinstrs < %s | FileCheck %s +; A collection of basic functionality tests for statepoint lowering - most +; interesting cornercases are exercised through the x86 tests. + +target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "riscv64" + +%struct = type { i64, i64 } + +declare zeroext i1 @return_i1() +declare zeroext i32 @return_i32() +declare ptr @return_i32ptr() +declare float @return_float() +declare %struct @return_struct() +declare void @varargf(i32, ...) + +define i1 @test_i1_return() gc "statepoint-example" { +; CHECK-LABEL: test_i1_return: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: addi sp, sp, -16 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: call return_i1 +; CHECK-NEXT: .Ltmp0: +; CHECK-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; CHECK-NEXT: addi sp, sp, 16 +; CHECK-NEXT: ret +; This is just checking that a i1 gets lowered normally when there's no extra +; state arguments to the statepoint +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) + %call1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) + ret i1 %call1 +} + +define i32 @test_i32_return() gc "statepoint-example" { +; CHECK-LABEL: test_i32_return: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: addi sp, sp, -16 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: call return_i32 +; CHECK-NEXT: .Ltmp1: +; CHECK-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; CHECK-NEXT: addi sp, sp, 16 +; CHECK-NEXT: ret +entry: + %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(i32 ()) @return_i32, i32 0, i32 0, i32 0, i32 0) + %call1 = call zeroext i32 @llvm.experimental.gc.result.i32(token %safepoint_token) + ret i32 %call1 +} + +define ptr @test_i32ptr_return() gc "statepoint-example" { +; CHECK-LABEL: test_i32ptr_return: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: addi sp, sp, -16 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: call return_i32ptr +; CHECK-NEXT: .Ltmp2: +; CHECK-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; CHECK-NEXT: addi sp, sp, 16 +; CHECK-NEXT: ret +entry: + %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(ptr ()) @return_i32ptr, i32 0, i32 0, i32 0, i32 0) + %call1 = call ptr @llvm.experimental.gc.result.p0(token %safepoint_token) + ret ptr %call1 +} + +define float @test_float_return() gc "statepoint-example" { +; CHECK-LABEL: test_float_return: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: addi sp, sp, -16 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: call return_float +; CHECK-NEXT: .Ltmp3: +; CHECK-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; CHECK-NEXT: addi sp, sp, 16 +; CHECK-NEXT: ret +entry: + %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(float ()) @return_float, i32 0, i32 0, i32 0, i32 0) + %call1 = call float @llvm.experimental.gc.result.f32(token %safepoint_token) + ret float %call1 +} + +define %struct @test_struct_return() gc "statepoint-example" { +; CHECK-LABEL: test_struct_return: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: addi sp, sp, -16 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: call return_struct +; CHECK-NEXT: .Ltmp4: +; CHECK-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; CHECK-NEXT: addi sp, sp, 16 +; CHECK-NEXT: ret +entry: + %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(%struct ()) @return_struct, i32 0, i32 0, i32 0, i32 0) + %call1 = call %struct @llvm.experimental.gc.result.struct(token %safepoint_token) + ret %struct %call1 +} + +define i1 @test_relocate(ptr addrspace(1) %a) gc "statepoint-example" { +; CHECK-LABEL: test_relocate: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: addi sp, sp, -16 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: sd a0, 0(sp) +; CHECK-NEXT: call return_i1 +; CHECK-NEXT: .Ltmp5: +; CHECK-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; CHECK-NEXT: addi sp, sp, 16 +; CHECK-NEXT: ret +; Check that an ununsed relocate has no code-generation impact +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)] + %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) + ret i1 %call2 +} + +define void @test_void_vararg() gc "statepoint-example" { +; CHECK-LABEL: test_void_vararg: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: addi sp, sp, -16 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: li a0, 42 +; CHECK-NEXT: li a1, 43 +; CHECK-NEXT: call varargf +; CHECK-NEXT: .Ltmp6: +; CHECK-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; CHECK-NEXT: addi sp, sp, 16 +; CHECK-NEXT: ret +; Check a statepoint wrapping a *ptr returning vararg function works +entry: + %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void (i32, ...)) @varargf, i32 2, i32 0, i32 42, i32 43, i32 0, i32 0) + ;; if we try to use the result from a statepoint wrapping a + ;; non-void-returning varargf, we will experience a crash. + ret void +} + +define i1 @test_i1_return_patchable() gc "statepoint-example" { +; CHECK-LABEL: test_i1_return_patchable: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: addi sp, sp, -16 +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: sd ra, 8(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: nop +; CHECK-NEXT: .Ltmp7: +; CHECK-NEXT: ld ra, 8(sp) # 8-byte Folded Reload +; CHECK-NEXT: addi sp, sp, 16 +; CHECK-NEXT: ret +; A patchable variant of test_i1_return +entry: + %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 4, ptr elementtype(i1 ()) null, i32 0, i32 0, i32 0, i32 0) + %call1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) + ret i1 %call1 +} + +declare void @consume(ptr addrspace(1) %obj) + +define i1 @test_cross_bb(ptr addrspace(1) %a, i1 %external_cond) gc "statepoint-example" { +; CHECK-LABEL: test_cross_bb: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: addi sp, sp, -32 +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: sd ra, 24(sp) # 8-byte Folded Spill +; CHECK-NEXT: sd s0, 16(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: .cfi_offset s0, -16 +; CHECK-NEXT: andi s0, a1, 1 +; CHECK-NEXT: sd a0, 8(sp) +; CHECK-NEXT: call return_i1 +; CHECK-NEXT: .Ltmp8: +; CHECK-NEXT: beqz s0, .LBB8_2 +; CHECK-NEXT: # %bb.1: # %left +; CHECK-NEXT: ld a1, 8(sp) +; CHECK-NEXT: mv s0, a0 +; CHECK-NEXT: mv a0, a1 +; CHECK-NEXT: call consume +; CHECK-NEXT: mv a0, s0 +; CHECK-NEXT: j .LBB8_3 +; CHECK-NEXT: .LBB8_2: # %right +; CHECK-NEXT: li a0, 1 +; CHECK-NEXT: .LBB8_3: # %right +; CHECK-NEXT: ld ra, 24(sp) # 8-byte Folded Reload +; CHECK-NEXT: ld s0, 16(sp) # 8-byte Folded Reload +; CHECK-NEXT: addi sp, sp, 32 +; CHECK-NEXT: ret +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)] + br i1 %external_cond, label %left, label %right + +left: + %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 + +right: + ret i1 true +} + +%struct2 = type { i64, i64, i64 } + +declare void @consume_attributes(i32, ptr nest, i32, ptr byval(%struct2)) + +define void @test_attributes(ptr byval(%struct2) %s) gc "statepoint-example" { +; CHECK-LABEL: test_attributes: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: addi sp, sp, -32 +; CHECK-NEXT: .cfi_def_cfa_offset 32 +; CHECK-NEXT: sd ra, 24(sp) # 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset ra, -8 +; CHECK-NEXT: ld a1, 16(a0) +; CHECK-NEXT: sd a1, 16(sp) +; CHECK-NEXT: ld a1, 8(a0) +; CHECK-NEXT: sd a1, 8(sp) +; CHECK-NEXT: ld a0, 0(a0) +; CHECK-NEXT: sd a0, 0(sp) +; CHECK-NEXT: li a0, 42 +; CHECK-NEXT: li a1, 17 +; CHECK-NEXT: mv a2, sp +; CHECK-NEXT: li t2, 0 +; CHECK-NEXT: call consume_attributes +; CHECK-NEXT: .Ltmp9: +; CHECK-NEXT: ld ra, 24(sp) # 8-byte Folded Reload +; CHECK-NEXT: addi sp, sp, 32 +; CHECK-NEXT: ret +entry: +; Check that arguments with attributes are lowered correctly. +; We call a function that has a nest argument and a byval argument. + %statepoint_token = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void (i32, ptr, i32, ptr)) @consume_attributes, i32 4, i32 0, i32 42, ptr nest null, i32 17, ptr byval(%struct2) %s, i32 0, i32 0) + ret void +} + +declare token @llvm.experimental.gc.statepoint.p0(i64, i32, ptr, i32, i32, ...) +declare i1 @llvm.experimental.gc.result.i1(token) + +declare i32 @llvm.experimental.gc.result.i32(token) + +declare ptr @llvm.experimental.gc.result.p0(token) + +declare float @llvm.experimental.gc.result.f32(token) + +declare %struct @llvm.experimental.gc.result.struct(token) + + + +declare ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token, i32, i32) From c69bc2ce378a2a4960d32b42f8310ae0ee28fdc3 Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Tue, 2 Apr 2024 13:42:15 +0200 Subject: [PATCH 4/6] Change comment about no patch bytes in statepoint --- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index 5837a74d3f6a67..9aeae0405a23e3 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -1472,7 +1472,7 @@ unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { case TargetOpcode::STATEPOINT: { // The size of the statepoint intrinsic is the number of bytes requested unsigned NumBytes = StatepointOpers(&MI).getNumPatchBytes(); - // A statepoint is at least a PseudoCALL + // No patch bytes means a PseudoCall is emitted return std::max(NumBytes, 8U); } default: From 1853bde29abe0f22de8d4834192f4dea14c273c1 Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Thu, 4 Apr 2024 19:54:16 +0200 Subject: [PATCH 5/6] Use the correct size for compressed instructions --- llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp | 20 +++++++++++++------- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp index 6a727f250521b7..5bf1e0e739ac2a 100644 --- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp +++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -165,14 +165,20 @@ void RISCVAsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM, // Materialize the jump address: SmallVector Seq; RISCVMatInt::generateMCInstSeq(CallTarget, *STI, RISCV::X1, Seq); - for (MCInst &Inst : Seq) + for (MCInst &Inst : Seq) { + MCInst CInst; + bool compressed = RISCVRVC::compress(CInst, Inst, *STI); EmitToStreamer(OutStreamer, Inst); - EncodedBytes += Seq.size() * 4; - EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JALR) - .addReg(RISCV::X1) - .addReg(RISCV::X1) - .addImm(0)); - EncodedBytes += 4; + EncodedBytes += compressed ? 2 : 4; + } + MCInst CInst; + MCInst Inst = MCInstBuilder(RISCV::JALR) + .addReg(RISCV::X1) + .addReg(RISCV::X1) + .addImm(0); + bool compressed = RISCVRVC::compress(CInst, Inst, *STI); + EmitToStreamer(OutStreamer, Inst); + EncodedBytes += compressed ? 2 : 4; } } else if (CalleeMO.isGlobal()) { MCOperand CallTargetMCOp; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index 9aeae0405a23e3..64845eda2ed2f8 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -1472,7 +1472,7 @@ unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { case TargetOpcode::STATEPOINT: { // The size of the statepoint intrinsic is the number of bytes requested unsigned NumBytes = StatepointOpers(&MI).getNumPatchBytes(); - // No patch bytes means a PseudoCall is emitted + // No patch bytes means at most a PseudoCall is emitted return std::max(NumBytes, 8U); } default: From bbd31c899909760d72d3d43a3e5b37b973d0064a Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Tue, 9 Apr 2024 14:37:33 +0200 Subject: [PATCH 6/6] compressed -> Compressed and use return value from EmitToStreamer --- llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp index 5bf1e0e739ac2a..779f179dff619f 100644 --- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp +++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -166,19 +166,14 @@ void RISCVAsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM, SmallVector Seq; RISCVMatInt::generateMCInstSeq(CallTarget, *STI, RISCV::X1, Seq); for (MCInst &Inst : Seq) { - MCInst CInst; - bool compressed = RISCVRVC::compress(CInst, Inst, *STI); - EmitToStreamer(OutStreamer, Inst); - EncodedBytes += compressed ? 2 : 4; + bool Compressed = EmitToStreamer(OutStreamer, Inst); + EncodedBytes += Compressed ? 2 : 4; } - MCInst CInst; - MCInst Inst = MCInstBuilder(RISCV::JALR) - .addReg(RISCV::X1) - .addReg(RISCV::X1) - .addImm(0); - bool compressed = RISCVRVC::compress(CInst, Inst, *STI); - 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;