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 @@ -23,8 +23,10 @@ #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/StackMaps.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/TargetRegistry.h" @@ -39,17 +41,27 @@ namespace { class RISCVAsmPrinter : public AsmPrinter { const MCSubtargetInfo *MCSTI; + StackMaps SM; const RISCVSubtarget *STI; public: explicit RISCVAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) - : AsmPrinter(TM, std::move(Streamer)), MCSTI(TM.getMCSubtargetInfo()) {} + : AsmPrinter(TM, std::move(Streamer)), MCSTI(TM.getMCSubtargetInfo()), SM(*this) {} StringRef getPassName() const override { return "RISCV Assembly Printer"; } bool runOnMachineFunction(MachineFunction &MF) override; + void LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM, + const MachineInstr &MI); + + void LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM, + const MachineInstr &MI); + + void LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM, + const MachineInstr &MI); + void emitInstruction(const MachineInstr *MI) override; bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, @@ -73,9 +85,158 @@ private: void emitAttributes(); + void emitCallProlog(MCStreamer &OutStreamer); + void emitCallEpilog(MCStreamer &OutStreamer); }; } +void RISCVAsmPrinter::emitCallProlog(MCStreamer &OutStreamer) { + EmitToStreamer(OutStreamer, + MCInstBuilder(RISCV::ADDI).addReg(RISCV::X2).addReg(RISCV::X2).addImm(-8)); + EmitToStreamer(OutStreamer, + MCInstBuilder(RISCV::SD).addReg(RISCV::X1).addReg(RISCV::X2).addImm(0)); +} + +void RISCVAsmPrinter::emitCallEpilog(MCStreamer &OutStreamer) { + EmitToStreamer(OutStreamer, + MCInstBuilder(RISCV::LD).addReg(RISCV::X1).addReg(RISCV::X2).addImm(0)); + EmitToStreamer(OutStreamer, + MCInstBuilder(RISCV::ADDI).addReg(RISCV::X2).addReg(RISCV::X2).addImm(8)); +} + +void RISCVAsmPrinter::LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM, + const MachineInstr &MI) { + unsigned NumNOPBytes = StackMapOpers(&MI).getNumPatchBytes(); + + auto &Ctx = OutStreamer.getContext(); + MCSymbol *MILabel = Ctx.createTempSymbol(); + OutStreamer.emitLabel(MILabel); + + SM.recordStackMap(*MILabel, MI); + assert(NumNOPBytes % 4 == 0 && "Invalid number of NOP bytes requested!"); + + // Scan ahead to trim the shadow. + const MachineBasicBlock &MBB = *MI.getParent(); + MachineBasicBlock::const_iterator MII(MI); + ++MII; + while (NumNOPBytes > 0) { + if (MII == MBB.end() || MII->isCall() || + MII->getOpcode() == RISCV::DBG_VALUE || + MII->getOpcode() == TargetOpcode::PATCHPOINT || + MII->getOpcode() == TargetOpcode::STACKMAP) + break; + ++MII; + NumNOPBytes -= 4; + } + + // Emit nops. + for (unsigned i = 0; i < NumNOPBytes; i += 4) + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::ADDI).addReg(RISCV::X0).addReg(RISCV::X0).addImm(0)); +} + +// Lower a patchpoint of the form: +// [], , , , +void RISCVAsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM, + const MachineInstr &MI) { + auto &Ctx = OutStreamer.getContext(); + MCSymbol *MILabel = Ctx.createTempSymbol(); + OutStreamer.emitLabel(MILabel); + SM.recordPatchPoint(*MILabel, MI); + + PatchPointOpers Opers(&MI); + + int64_t CallTarget = Opers.getCallTarget().getImm(); + unsigned EncodedBytes = 0; + if (CallTarget) { + assert((CallTarget & 0xFFFFFFFFFFFF) == CallTarget && + "High 16 bits of call target should be zero."); + EncodedBytes = 16; + // Materialize the jump address: + + emitCallProlog(OutStreamer); + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::ORI) + .addReg(RISCV::X1) + .addReg(RISCV::X0) + .addImm((CallTarget >> 36) & 0xFFF)); + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::SLLI) + .addReg(RISCV::X1) + .addReg(RISCV::X1) + .addImm(12)); + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::ORI) + .addReg(RISCV::X1) + .addReg(RISCV::X1) + .addImm((CallTarget >> 24) & 0xFFF)); + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::SLLI) + .addReg(RISCV::X1) + .addReg(RISCV::X1) + .addImm(12)); + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::ORI) + .addReg(RISCV::X1) + .addReg(RISCV::X1) + .addImm((CallTarget >> 12) & 0xFFF)); + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::SLLI) + .addReg(RISCV::X1) + .addReg(RISCV::X1) + .addImm(12)); + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::ORI) + .addReg(RISCV::X1) + .addReg(RISCV::X1) + .addImm(CallTarget & 0xFFF)); + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JALR).addReg(RISCV::X1).addReg(RISCV::X1).addImm(0)); + emitCallEpilog(OutStreamer); + } + // Emit padding. + unsigned NumBytes = Opers.getNumPatchBytes(); + assert(NumBytes >= EncodedBytes && + "Patchpoint can't request size less than the length of a call."); + assert((NumBytes - EncodedBytes) % 4 == 0 && + "Invalid number of NOP bytes requested!"); + for (unsigned i = EncodedBytes; i < NumBytes; i += 4) + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::ADDI).addReg(RISCV::X0).addReg(RISCV::X0).addImm(0)); +} + +void RISCVAsmPrinter::LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM, + const MachineInstr &MI) { + StatepointOpers SOpers(&MI); + if (unsigned PatchBytes = SOpers.getNumPatchBytes()) { + assert(PatchBytes % 4 == 0 && "Invalid number of NOP bytes requested!"); + for (unsigned i = 0; i < PatchBytes; i += 4) + EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::ADDI).addReg(RISCV::X0).addReg(RISCV::X0).addImm(0)); + } else { + // Lower call target and choose correct opcode + const MachineOperand &CallTarget = SOpers.getCallTarget(); + MCOperand CallTargetMCOp; + emitCallProlog(OutStreamer); + 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; + } + emitCallEpilog(OutStreamer); + } + + auto &Ctx = OutStreamer.getContext(); + MCSymbol *MILabel = Ctx.createTempSymbol(); + OutStreamer.emitLabel(MILabel); + SM.recordStatepoint(*MILabel, MI); +} + #define GEN_COMPRESS_INSTR #include "RISCVGenCompressInstEmitter.inc" void RISCVAsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst) { @@ -96,8 +257,20 @@ return; MCInst TmpInst; - if (!lowerRISCVMachineInstrToMCInst(MI, TmpInst, *this)) - EmitToStreamer(*OutStreamer, TmpInst); + switch (MI->getOpcode()) { + default: + if (!lowerRISCVMachineInstrToMCInst(MI, TmpInst, *this)) + EmitToStreamer(*OutStreamer, TmpInst); + return; + case TargetOpcode::STACKMAP: + return LowerSTACKMAP(*OutStreamer, SM, *MI); + + case TargetOpcode::PATCHPOINT: + return LowerPATCHPOINT(*OutStreamer, SM, *MI); + + case TargetOpcode::STATEPOINT: + return LowerSTATEPOINT(*OutStreamer, SM, *MI); + } } bool RISCVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, @@ -192,6 +365,8 @@ if (TM.getTargetTriple().isOSBinFormatELF()) RTS.finishAttributeSection(); + + emitStackMaps(SM); } void RISCVAsmPrinter::emitAttributes() { 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 @@ -9523,6 +9523,10 @@ return emitQuietFCMP(MI, BB, RISCV::FLE_D, RISCV::FEQ_D, Subtarget); case RISCV::PseudoQuietFLT_D: return emitQuietFCMP(MI, BB, RISCV::FLT_D, RISCV::FEQ_D, Subtarget); + case TargetOpcode::STACKMAP: + case TargetOpcode::PATCHPOINT: + case TargetOpcode::STATEPOINT: + return emitPatchPoint(MI, BB); } } diff --git a/llvm/test/CodeGen/RISCV/rv64-patchpoint.ll b/llvm/test/CodeGen/RISCV/rv64-patchpoint.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/rv64-patchpoint.ll @@ -0,0 +1,80 @@ +; 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: trivial_patchpoint_codegen: +; CHECK: addi sp, sp, -8 +; CHECK-NEXT: sd ra, 0(sp) +; CHECK-NEXT: ori ra, zero, 3562 +; CHECK-NEXT: slli ra, ra, 12 +; CHECK-NEXT: ori ra, ra, 3518 +; CHECK-NEXT: slli ra, ra, 12 +; CHECK-NEXT: ori ra, ra, 3836 +; CHECK-NEXT: slli ra, ra, 12 +; CHECK-NEXT: ori ra, ra, 2814 +; CHECK-NEXT: jalr ra +; CHECK-NEXT: ld ra, 0(sp) +; CHECK-NEXT: addi sp, sp, 8 + +; CHECK: addi sp, sp, -8 +; CHECK-NEXT: sd ra, 0(sp) +; CHECK-NEXT: ori ra, zero, 3562 +; CHECK-NEXT: slli ra, ra, 12 +; CHECK-NEXT: ori ra, ra, 3518 +; CHECK-NEXT: slli ra, ra, 12 +; CHECK-NEXT: ori ra, ra, 3836 +; CHECK-NEXT: slli ra, ra, 12 +; CHECK-NEXT: ori ra, ra, 2815 +; CHECK-NEXT: jalr ra +; CHECK-NEXT: ld ra, 0(sp) +; CHECK-NEXT: addi sp, sp, 8 +; CHECK: ret + %resolveCall2 = inttoptr i64 244837814094590 to i8* + %result = tail call i64 (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.i64(i64 2, i32 20, 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 20, 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: +; CHECK-LABEL: small_patchpoint_codegen: +; CHECK: Ltmp +; CHECK: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: ret + %result = tail call i64 (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.i64(i64 5, i32 20, i8* null, i32 2, i64 %p1, i64 %p2) + ret void +} + +declare void @llvm.experimental.stackmap(i64, i32, ...) +declare void @llvm.experimental.patchpoint.void(i64, i32, i8*, i32, ...) +declare i64 @llvm.experimental.patchpoint.i64(i64, i32, i8*, i32, ...) + diff --git a/llvm/test/CodeGen/RISCV/rv64-stackmap-nops.ll b/llvm/test/CodeGen/RISCV/rv64-stackmap-nops.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/rv64-stackmap-nops.ll @@ -0,0 +1,13 @@ +; RUN: llc < %s -mtriple=riscv64 | FileCheck %s + +define void @test_shadow_optimization() { +entry: +; CHECK-LABEL: test_shadow_optimization: +; CHECK: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop + tail call void (i64, i32, ...) @llvm.experimental.stackmap(i64 0, i32 16) + ret void +} + +declare void @llvm.experimental.stackmap(i64, i32, ...) diff --git a/llvm/test/CodeGen/RISCV/rv64-stackmap.ll b/llvm/test/CodeGen/RISCV/rv64-stackmap.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/rv64-stackmap.ll @@ -0,0 +1,261 @@ +; RUN: llc -mtriple=riscv64 < %s | FileCheck %s +; RUN: llc -mtriple=riscv64 -fast-isel -fast-isel-abort=1 < %s | FileCheck %s +; +; Note: Print verbose stackmaps using -debug-only=stackmaps. + +; CHECK-LABEL: .section .llvm_stackmaps +; CHECK-NEXT: __LLVM_StackMaps: +; Header +; CHECK-NEXT: .byte 3 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 0 +; Num Functions +; CHECK-NEXT: .word 8 +; Num LargeConstants +; CHECK-NEXT: .word 2 +; Num Callsites +; CHECK-NEXT: .word 8 + +; Functions and stack size +; CHECK-NEXT: .quad constantargs +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad osrinline +; CHECK-NEXT: .quad 32 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad osrcold +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad propertyRead +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad propertyWrite +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad jsVoidCall +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad jsIntCall +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad liveConstant +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 + +; Num LargeConstants +; CHECK-NEXT: .quad 4294967295 +; CHECK-NEXT: .quad 4294967296 + +; Constant arguments +; +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .word .L{{.*}}-constantargs +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 4 +; SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 65535 +; SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 65536 +; SmallConstant +; CHECK-NEXT: .byte 5 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; LargeConstant at index 0 +; CHECK-NEXT: .byte 5 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 1 + +define void @constantargs() { +entry: + %0 = inttoptr i64 244837814094590 to i8* + tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 1, i32 20, i8* %0, i32 0, i64 65535, i64 65536, i64 4294967295, i64 4294967296) + ret void +} + +; Inline OSR Exit +; +; CHECK: .word .L{{.*}}-osrinline +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +define void @osrinline(i64 %a, i64 %b) { +entry: + ; Runtime void->void call. + call void inttoptr (i64 244837814094590 to void ()*)() + ; Followed by inline OSR patchpoint with 12-byte shadow and 2 live vars. + call void (i64, i32, ...) @llvm.experimental.stackmap(i64 3, i32 12, i64 %a, i64 %b) + ret void +} + +; Cold OSR Exit +; +; 2 live variables in register. +; +; CHECK: .word .L{{.*}}-osrcold +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +define void @osrcold(i64 %a, i64 %b) { +entry: + %test = icmp slt i64 %a, %b + br i1 %test, label %ret, label %cold +cold: + ; OSR patchpoint with 12-byte nop-slide and 2 live vars. + %thunk = inttoptr i64 244837814094590 to i8* + call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 4, i32 20, i8* %thunk, i32 0, i64 %a, i64 %b) + unreachable +ret: + ret void +} + +; Property Read +; CHECK: .word .L{{.*}}-propertyRead +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; +; FIXME: There are currently no stackmap entries. After moving to +; AnyRegCC, we will have entries for the object and return value. +define i64 @propertyRead(i64* %obj) { +entry: + %resolveRead = inttoptr i64 244837814094590 to i8* + %result = call i64 (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.i64(i64 5, i32 20, i8* %resolveRead, i32 1, i64* %obj) + %add = add i64 %result, 3 + ret i64 %add +} + +; Property Write +; CHECK: .word .L{{.*}}-propertyWrite +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +define void @propertyWrite(i64 %dummy1, i64* %obj, i64 %dummy2, i64 %a) { +entry: + %resolveWrite = inttoptr i64 244837814094590 to i8* + call anyregcc void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 6, i32 20, i8* %resolveWrite, i32 2, i64* %obj, i64 %a) + ret void +} + +; Void JS Call +; +; 2 live variables in registers. +; +; CHECK: .word .L{{.*}}-jsVoidCall +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +define void @jsVoidCall(i64 %dummy1, i64* %obj, i64 %arg, i64 %l1, i64 %l2) { +entry: + %resolveCall = inttoptr i64 244837814094590 to i8* + call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 7, i32 20, i8* %resolveCall, i32 2, i64* %obj, i64 %arg, i64 %l1, i64 %l2) + ret void +} + +; i64 JS Call +; +; 2 live variables in registers. +; +; CHECK: .word .L{{.*}}-jsIntCall +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +define i64 @jsIntCall(i64 %dummy1, i64* %obj, i64 %arg, i64 %l1, i64 %l2) { +entry: + %resolveCall = inttoptr i64 244837814094590 to i8* + %result = call i64 (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.i64(i64 8, i32 20, i8* %resolveCall, i32 2, i64* %obj, i64 %arg, i64 %l1, i64 %l2) + %add = add i64 %result, 3 + ret i64 %add +} + +; Map a constant value. +; +; CHECK: .word .L{{.*}}-liveConstant +; CHECK-NEXT: .half 0 +; 1 location +; CHECK-NEXT: .half 1 +; Loc 0: SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 33 + +define void @liveConstant() { + tail call void (i64, i32, ...) @llvm.experimental.stackmap(i64 15, i32 8, i32 33) + ret void +} + +declare void @llvm.experimental.stackmap(i64, i32, ...) +declare void @llvm.experimental.patchpoint.void(i64, i32, i8*, i32, ...) +declare i64 @llvm.experimental.patchpoint.i64(i64, i32, i8*, i32, ...) diff --git a/llvm/test/CodeGen/RISCV/stackmap-frame-setup.ll b/llvm/test/CodeGen/RISCV/stackmap-frame-setup.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/stackmap-frame-setup.ll @@ -0,0 +1,20 @@ +; RUN: llc -o - -verify-machineinstrs -mtriple=riscv64 -stop-after machine-sink %s | FileCheck %s --check-prefix=ISEL +; RUN: llc -o - -verify-machineinstrs -mtriple=riscv64 -fast-isel -fast-isel-abort=1 -stop-after machine-sink %s | FileCheck %s --check-prefix=FAST-ISEL + +define void @caller_meta_leaf() { +entry: + %metadata = alloca i64, i32 3, align 8 + store i64 11, i64* %metadata + store i64 12, i64* %metadata + store i64 13, i64* %metadata +; ISEL: ADJCALLSTACKDOWN 0, 0, implicit-def +; ISEL-NEXT: STACKMAP +; ISEL-NEXT: ADJCALLSTACKUP 0, 0, implicit-def + call void (i64, i32, ...) @llvm.experimental.stackmap(i64 4, i32 0, i64* %metadata) +; FAST-ISEL: ADJCALLSTACKDOWN 0, 0, implicit-def +; FAST-ISEL-NEXT: STACKMAP +; FAST-ISEL-NEXT: ADJCALLSTACKUP 0, 0, implicit-def + ret void +} + +declare void @llvm.experimental.stackmap(i64, i32, ...) diff --git a/llvm/test/CodeGen/RISCV/stackmap.ll b/llvm/test/CodeGen/RISCV/stackmap.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/stackmap.ll @@ -0,0 +1,444 @@ +; RUN: llc < %s -mtriple=riscv64 | FileCheck %s +; +; Note: Print verbose stackmaps using -debug-only=stackmaps. + +; CHECK-LABEL: .section .llvm_stackmaps +; CHECK-NEXT: __LLVM_StackMaps: +; Header +; CHECK-NEXT: .byte 3 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 0 +; Num Functions +; CHECK-NEXT: .word 12 +; Num LargeConstants +; CHECK-NEXT: .word 3 +; Num Callsites +; CHECK-NEXT: .word 16 + +; Functions and stack size +; CHECK-NEXT: .quad constantargs +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad osrinline +; CHECK-NEXT: .quad 32 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad osrcold +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad propertyRead +; CHECK-NEXT: .quad 16 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad propertyWrite +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad jsVoidCall +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad jsIntCall +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad spilledValue +; CHECK-NEXT: .quad 144 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad liveConstant +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .quad directFrameIdx +; CHECK-NEXT: .quad 48 +; CHECK-NEXT: .quad 2 +; CHECK-NEXT: .quad longid +; CHECK-NEXT: .quad 0 +; CHECK-NEXT: .quad 4 +; CHECK-NEXT: .quad needsStackRealignment +; CHECK-NEXT: .quad -1 +; CHECK-NEXT: .quad 1 + +; Large Constants +; CHECK-NEXT: .quad 2147483648 +; CHECK-NEXT: .quad 4294967295 +; CHECK-NEXT: .quad 4294967296 + +; Callsites +; Constant arguments +; +; CHECK-NEXT: .quad 1 +; CHECK-NEXT: .word .L{{.*}}-constantargs +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 12 +; SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word -1 +; SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word -1 +; SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 65536 +; SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 2000000000 +; SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 2147483647 +; SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word -1 +; SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word -1 +; SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; LargeConstant at index 0 +; CHECK-NEXT: .byte 5 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; LargeConstant at index 1 +; CHECK-NEXT: .byte 5 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 1 +; LargeConstant at index 2 +; CHECK-NEXT: .byte 5 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 2 +; SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word -1 + +define void @constantargs() { +entry: + %0 = inttoptr i64 12345 to i8* + tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 1, i32 16, i8* %0, i32 0, i16 65535, i16 -1, i32 65536, i32 2000000000, i32 2147483647, i32 -1, i32 4294967295, i32 4294967296, i64 2147483648, i64 4294967295, i64 4294967296, i64 -1) + ret void +} + +; Inline OSR Exit +; +; CHECK-LABEL: .word .L{{.*}}-osrinline +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +define void @osrinline(i64 %a, i64 %b) { +entry: + ; Runtime void->void call. + call void inttoptr (i64 -559038737 to void ()*)() + ; Followed by inline OSR patchpoint with 12-byte shadow and 2 live vars. + call void (i64, i32, ...) @llvm.experimental.stackmap(i64 3, i32 12, i64 %a, i64 %b) + ret void +} + +; Cold OSR Exit +; +; 2 live variables in register. +; +; CHECK-LABEL: .word .L{{.*}}-osrcold +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +define void @osrcold(i64 %a, i64 %b) { +entry: + %test = icmp slt i64 %a, %b + br i1 %test, label %ret, label %cold +cold: + ; OSR patchpoint with 12-byte nop-slide and 2 live vars. + %thunk = inttoptr i64 3735928559 to i8* + call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 4, i32 16, i8* %thunk, i32 0, i64 %a, i64 %b) + unreachable +ret: + ret void +} + +; Property Read +; CHECK-LABEL: .word .L{{.*}}-propertyRead +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +define i64 @propertyRead(i64* %obj) { +entry: + %resolveRead = inttoptr i64 3735928559 to i8* + %result = call anyregcc i64 (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.i64(i64 5, i32 16, i8* %resolveRead, i32 1, i64* %obj) + %add = add i64 %result, 3 + ret i64 %add +} + +; Property Write +; CHECK-LABEL: .word .L{{.*}}-propertyWrite +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +define void @propertyWrite(i64 %dummy1, i64* %obj, i64 %dummy2, i64 %a) { +entry: + %resolveWrite = inttoptr i64 3735928559 to i8* + call anyregcc void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 6, i32 16, i8* %resolveWrite, i32 2, i64* %obj, i64 %a) + ret void +} + +; Void JS Call +; +; 2 live variables in registers. +; +; CHECK-LABEL: .word .L{{.*}}-jsVoidCall +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +define void @jsVoidCall(i64 %dummy1, i64* %obj, i64 %arg, i64 %l1, i64 %l2) { +entry: + %resolveCall = inttoptr i64 3735928559 to i8* + call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 7, i32 16, i8* %resolveCall, i32 2, i64* %obj, i64 %arg, i64 %l1, i64 %l2) + ret void +} + +; i64 JS Call +; +; 2 live variables in registers. +; +; CHECK-LABEL: .word .L{{.*}}-jsIntCall +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +; CHECK-NEXT: .byte 1 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half {{[0-9]+}} +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 0 +define i64 @jsIntCall(i64 %dummy1, i64* %obj, i64 %arg, i64 %l1, i64 %l2) { +entry: + %resolveCall = inttoptr i64 3735928559 to i8* + %result = call i64 (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.i64(i64 8, i32 16, i8* %resolveCall, i32 2, i64* %obj, i64 %arg, i64 %l1, i64 %l2) + %add = add i64 %result, 3 + ret i64 %add +} + +; Spilled stack map values. +; +; Verify 28 stack map entries. +; +; CHECK-LABEL: .word .L{{.*}}-spilledValue +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 28 +; +; Check that at least one is a spilled entry from RBP. +; Location: Indirect RBP + ... +; CHECK: .byte 3 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word +define void @spilledValue(i64 %arg0, i64 %arg1, i64 %arg2, i64 %arg3, i64 %arg4, i64 %l0, i64 %l1, i64 %l2, i64 %l3, i64 %l4, i64 %l5, i64 %l6, i64 %l7, i64 %l8, i64 %l9, i64 %l10, i64 %l11, i64 %l12, i64 %l13, i64 %l14, i64 %l15, i64 %l16, i64 %l17, i64 %l18, i64 %l19, i64 %l20, i64 %l21, i64 %l22, i64 %l23, i64 %l24, i64 %l25, i64 %l26, i64 %l27) { +entry: + call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 11, i32 20, i8* null, i32 5, i64 %arg0, i64 %arg1, i64 %arg2, i64 %arg3, i64 %arg4, i64 %l0, i64 %l1, i64 %l2, i64 %l3, i64 %l4, i64 %l5, i64 %l6, i64 %l7, i64 %l8, i64 %l9, i64 %l10, i64 %l11, i64 %l12, i64 %l13, i64 %l14, i64 %l15, i64 %l16, i64 %l17, i64 %l18, i64 %l19, i64 %l20, i64 %l21, i64 %l22, i64 %l23, i64 %l24, i64 %l25, i64 %l26, i64 %l27) + ret void +} + +; Map a constant value. +; +; CHECK-LABEL: .word .L{{.*}}-liveConstant +; CHECK-NEXT: .half 0 +; 1 location +; CHECK-NEXT: .half 1 +; Loc 0: SmallConstant +; CHECK-NEXT: .byte 4 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word 33 + +define void @liveConstant() { + tail call void (i64, i32, ...) @llvm.experimental.stackmap(i64 15, i32 4, i32 33) + ret void +} + +; Directly map an alloca's address. +; +; Callsite 16 +; CHECK-LABEL: .word .L{{.*}}-directFrameIdx +; CHECK-NEXT: .half 0 +; 1 location +; CHECK-NEXT: .half 1 +; Loc 0: Direct RBP - ofs +; CHECK-NEXT: .byte 2 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word + +; Callsite 17 +; CHECK-LABEL: .word .L{{.*}}-directFrameIdx +; CHECK-NEXT: .half 0 +; 2 locations +; CHECK-NEXT: .half 2 +; Loc 0: Direct RBP - ofs +; CHECK-NEXT: .byte 2 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word +; Loc 1: Direct RBP - ofs +; CHECK-NEXT: .byte 2 +; CHECK-NEXT: .byte 0 +; CHECK-NEXT: .half 8 +; CHECK-NEXT: .half 2 +; CHECK-NEXT: .half 0 +; CHECK-NEXT: .word +define void @directFrameIdx() { +entry: + %metadata1 = alloca i64, i32 3, align 8 + store i64 11, i64* %metadata1 + store i64 12, i64* %metadata1 + store i64 13, i64* %metadata1 + call void (i64, i32, ...) @llvm.experimental.stackmap(i64 16, i32 0, i64* %metadata1) + %metadata2 = alloca i8, i32 4, align 8 + %metadata3 = alloca i16, i32 4, align 8 + call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 17, i32 4, i8* null, i32 0, i8* %metadata2, i16* %metadata3) + ret void +} + +; Test a 64-bit ID. +; +; CHECK: .quad 4294967295 +; CHECK-LABEL: .word .L{{.*}}-longid +; CHECK: .quad 4294967296 +; CHECK-LABEL: .word .L{{.*}}-longid +; CHECK: .quad 9223372036854775807 +; CHECK-LABEL: .word .L{{.*}}-longid +; CHECK: .quad -1 +; CHECK-LABEL: .word .L{{.*}}-longid +define void @longid() { +entry: + tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 4294967295, i32 0, i8* null, i32 0) + tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 4294967296, i32 0, i8* null, i32 0) + tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 9223372036854775807, i32 0, i8* null, i32 0) + tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 -1, i32 0, i8* null, i32 0) + ret void +} + +; A stack frame which needs to be realigned at runtime (to meet alignment +; criteria for values on the stack) does not have a fixed frame size. +; CHECK-LABEL: .word .L{{.*}}-needsStackRealignment +; CHECK-NEXT: .half 0 +; 0 locations +; CHECK-NEXT: .half 0 +define void @needsStackRealignment() { + %val = alloca i64, i32 3, align 128 + tail call void (...) @escape_values(i64* %val) +; Note: Adding any non-constant to the stackmap would fail because we +; expected to be able to address off the frame pointer. In a realigned +; frame, we must use the stack pointer instead. This is a separate bug. + tail call void (i64, i32, ...) @llvm.experimental.stackmap(i64 0, i32 0) + ret void +} +declare void @escape_values(...) + +declare void @llvm.experimental.stackmap(i64, i32, ...) +declare void @llvm.experimental.patchpoint.void(i64, i32, i8*, i32, ...) +declare i64 @llvm.experimental.patchpoint.i64(i64, i32, i8*, i32, ...)