1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 02:52:53 +02:00

Add support for stack map generation in the X86 backend.

Originally implemented by Lang Hames.

llvm-svn: 193811
This commit is contained in:
Andrew Trick 2013-10-31 22:11:56 +00:00
parent 262df1db8e
commit 75681a41c0
10 changed files with 755 additions and 5 deletions

View File

@ -0,0 +1,107 @@
//===------------------- StackMaps.h - StackMaps ----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_STACKMAPS
#define LLVM_STACKMAPS
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineInstr.h"
#include <map>
#include <vector>
namespace llvm {
class AsmPrinter;
class MCExpr;
class StackMaps {
public:
struct Location {
enum LocationType { Unprocessed, Register, Direct, Indirect, Constant,
ConstantIndex };
LocationType LocType;
unsigned Reg;
int64_t Offset;
Location() : LocType(Unprocessed), Reg(0), Offset(0) {}
Location(LocationType LocType, unsigned Reg, int64_t Offset)
: LocType(LocType), Reg(Reg), Offset(Offset) {}
};
// Typedef a function pointer for functions that parse sequences of operands
// and return a Location, plus a new "next" operand iterator.
typedef std::pair<Location, MachineInstr::const_mop_iterator>
(*OperandParser)(MachineInstr::const_mop_iterator,
MachineInstr::const_mop_iterator);
// OpTypes are used to encode information about the following logical
// operand (which may consist of several MachineOperands) for the
// OpParser.
typedef enum { DirectMemRefOp, IndirectMemRefOp, ConstantOp } OpType;
StackMaps(AsmPrinter &AP, OperandParser OpParser)
: AP(AP), OpParser(OpParser) {}
/// This should be called by the MC lowering code _immediately_ before
/// lowering the MI to an MCInst. It records where the operands for the
/// instruction are stored, and outputs a label to record the offset of
/// the call from the start of the text section.
void recordStackMap(const MachineInstr &MI, uint32_t ID,
MachineInstr::const_mop_iterator MOI,
MachineInstr::const_mop_iterator MOE);
/// If there is any stack map data, create a stack map section and serialize
/// the map info into it. This clears the stack map data structures
/// afterwards.
void serializeToStackMapSection();
private:
typedef SmallVector<Location, 8> LocationVec;
struct CallsiteInfo {
const MCExpr *CSOffsetExpr;
unsigned ID;
LocationVec Locations;
CallsiteInfo() : CSOffsetExpr(0), ID(0) {}
CallsiteInfo(const MCExpr *CSOffsetExpr, unsigned ID,
LocationVec Locations)
: CSOffsetExpr(CSOffsetExpr), ID(ID), Locations(Locations) {}
};
typedef std::vector<CallsiteInfo> CallsiteInfoList;
struct ConstantPool {
private:
typedef std::map<int64_t, size_t> ConstantsMap;
std::vector<int64_t> ConstantsList;
ConstantsMap ConstantIndexes;
public:
size_t getNumConstants() const { return ConstantsList.size(); }
int64_t getConstant(size_t Idx) const { return ConstantsList[Idx]; }
size_t getConstantIndex(int64_t ConstVal) {
size_t NextIdx = ConstantsList.size();
ConstantsMap::const_iterator I =
ConstantIndexes.insert(ConstantIndexes.end(),
std::make_pair(ConstVal, NextIdx));
if (I->second == NextIdx)
ConstantsList.push_back(ConstVal);
return I->second;
}
};
AsmPrinter &AP;
OperandParser OpParser;
CallsiteInfoList CSInfos;
ConstantPool ConstPool;
};
}
#endif // LLVM_STACKMAPS

View File

@ -97,6 +97,7 @@ add_llvm_library(LLVMCodeGen
StackColoring.cpp
StackProtector.cpp
StackSlotColoring.cpp
StackMaps.cpp
TailDuplication.cpp
TargetFrameLoweringImpl.cpp
TargetInstrInfo.cpp

View File

@ -33,6 +33,7 @@
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/StackMaps.h"
#include "llvm/DebugInfo.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/Constants.h"
@ -6878,6 +6879,8 @@ void SelectionDAGBuilder::visitPatchpoint(const CallInst &CI) {
for (unsigned i = NumArgs + 4, e = CI.getNumArgOperands(); i != e; ++i) {
SDValue OpVal = getValue(CI.getArgOperand(i));
if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(OpVal)) {
Ops.push_back(
DAG.getTargetConstant(StackMaps::ConstantOp, MVT::i64));
Ops.push_back(
DAG.getTargetConstant(C->getSExtValue(), MVT::i64));
} else

213
lib/CodeGen/StackMaps.cpp Normal file
View File

@ -0,0 +1,213 @@
//===---------------------------- StackMaps.cpp ---------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "stackmaps"
#include "llvm/CodeGen/StackMaps.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetOpcodes.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include <iterator>
using namespace llvm;
void StackMaps::recordStackMap(const MachineInstr &MI, uint32_t ID,
MachineInstr::const_mop_iterator MOI,
MachineInstr::const_mop_iterator MOE) {
MCContext &OutContext = AP.OutStreamer.getContext();
MCSymbol *MILabel = OutContext.CreateTempSymbol();
AP.OutStreamer.EmitLabel(MILabel);
LocationVec CallsiteLocs;
while (MOI != MOE) {
std::pair<Location, MachineInstr::const_mop_iterator> ParseResult =
OpParser(MOI, MOE);
Location &Loc = ParseResult.first;
// Move large constants into the constant pool.
if (Loc.LocType == Location::Constant && (Loc.Offset & ~0xFFFFFFFFULL)) {
Loc.LocType = Location::ConstantIndex;
Loc.Offset = ConstPool.getConstantIndex(Loc.Offset);
}
CallsiteLocs.push_back(Loc);
MOI = ParseResult.second;
}
const MCExpr *CSOffsetExpr = MCBinaryExpr::CreateSub(
MCSymbolRefExpr::Create(MILabel, OutContext),
MCSymbolRefExpr::Create(AP.CurrentFnSym, OutContext),
OutContext);
CSInfos.push_back(CallsiteInfo(CSOffsetExpr, ID, CallsiteLocs));
}
/// serializeToStackMapSection conceptually populates the following fields:
///
/// uint32 : Reserved (header)
/// uint32 : NumConstants
/// int64 : Constants[NumConstants]
/// uint32 : NumRecords
/// StkMapRecord[NumRecords] {
/// uint32 : PatchPoint ID
/// uint32 : Instruction Offset
/// uint16 : Reserved (record flags)
/// uint16 : NumLocations
/// Location[NumLocations] {
/// uint8 : Register | Direct | Indirect | Constant | ConstantIndex
/// uint8 : Reserved (location flags)
/// uint16 : Dwarf RegNum
/// int32 : Offset
/// }
/// }
///
/// Location Encoding, Type, Value:
/// 0x1, Register, Reg (value in register)
/// 0x2, Direct, Reg + Offset (frame index)
/// 0x3, Indirect, [Reg + Offset] (spilled value)
/// 0x4, Constant, Offset (small constant)
/// 0x5, ConstIndex, Constants[Offset] (large constant)
///
void StackMaps::serializeToStackMapSection() {
// Bail out if there's no stack map data.
if (CSInfos.empty())
return;
MCContext &OutContext = AP.OutStreamer.getContext();
const TargetRegisterInfo *TRI = AP.TM.getRegisterInfo();
// Create the section.
const MCSection *StackMapSection =
OutContext.getMachOSection("__LLVM_STACKMAPS", "__llvm_stackmaps", 0,
SectionKind::getMetadata());
AP.OutStreamer.SwitchSection(StackMapSection);
// Emit a dummy symbol to force section inclusion.
AP.OutStreamer.EmitLabel(
OutContext.GetOrCreateSymbol(Twine("__LLVM_StackMaps")));
// Serialize data.
const char *WSMP = "Stack Maps: ";
const MCRegisterInfo &MCRI = *OutContext.getRegisterInfo();
DEBUG(dbgs() << "********** Stack Map Output **********\n");
// Header.
AP.OutStreamer.EmitIntValue(0, 4);
// Num constants.
AP.OutStreamer.EmitIntValue(ConstPool.getNumConstants(), 4);
// Constant pool entries.
for (unsigned i = 0; i < ConstPool.getNumConstants(); ++i)
AP.OutStreamer.EmitIntValue(ConstPool.getConstant(i), 8);
DEBUG(dbgs() << WSMP << "#callsites = " << CSInfos.size() << "\n");
AP.OutStreamer.EmitIntValue(CSInfos.size(), 4);
for (CallsiteInfoList::const_iterator CSII = CSInfos.begin(),
CSIE = CSInfos.end();
CSII != CSIE; ++CSII) {
unsigned CallsiteID = CSII->ID;
const LocationVec &CSLocs = CSII->Locations;
DEBUG(dbgs() << WSMP << "callsite " << CallsiteID << "\n");
// Verify stack map entry. It's better to communicate a problem to the
// runtime than crash in case of in-process compilation. Currently, we do
// simple overflow checks, but we may eventually communicate other
// compilation errors this way.
if (CSLocs.size() > UINT16_MAX) {
AP.OutStreamer.EmitIntValue(UINT32_MAX, 4); // Invalid ID.
AP.OutStreamer.EmitValue(CSII->CSOffsetExpr, 4);
AP.OutStreamer.EmitIntValue(0, 2); // Reserved.
AP.OutStreamer.EmitIntValue(0, 2); // 0 locations.
continue;
}
AP.OutStreamer.EmitIntValue(CallsiteID, 4);
AP.OutStreamer.EmitValue(CSII->CSOffsetExpr, 4);
// Reserved for flags.
AP.OutStreamer.EmitIntValue(0, 2);
DEBUG(dbgs() << WSMP << " has " << CSLocs.size() << " locations\n");
AP.OutStreamer.EmitIntValue(CSLocs.size(), 2);
unsigned operIdx = 0;
for (LocationVec::const_iterator LocI = CSLocs.begin(), LocE = CSLocs.end();
LocI != LocE; ++LocI, ++operIdx) {
const Location &Loc = *LocI;
DEBUG(
dbgs() << WSMP << " Loc " << operIdx << ": ";
switch (Loc.LocType) {
case Location::Unprocessed:
dbgs() << "<Unprocessed operand>";
break;
case Location::Register:
dbgs() << "Register " << MCRI.getName(Loc.Reg);
break;
case Location::Direct:
dbgs() << "Direct " << MCRI.getName(Loc.Reg);
if (Loc.Offset)
dbgs() << " + " << Loc.Offset;
break;
case Location::Indirect:
dbgs() << "Indirect " << MCRI.getName(Loc.Reg)
<< " + " << Loc.Offset;
break;
case Location::Constant:
dbgs() << "Constant " << Loc.Offset;
break;
case Location::ConstantIndex:
dbgs() << "Constant Index " << Loc.Offset;
break;
}
dbgs() << "\n";
);
unsigned RegNo = 0;
if(Loc.Reg) {
RegNo = MCRI.getDwarfRegNum(Loc.Reg, false);
for (MCSuperRegIterator SR(Loc.Reg, TRI);
SR.isValid() && (int)RegNo < 0; ++SR) {
RegNo = TRI->getDwarfRegNum(*SR, false);
}
}
else {
assert((Loc.LocType != Location::Register
&& Loc.LocType != Location::Register) &&
"Missing location register");
}
AP.OutStreamer.EmitIntValue(Loc.LocType, 1);
AP.OutStreamer.EmitIntValue(0, 1); // Reserved location flags.
AP.OutStreamer.EmitIntValue(RegNo, 2);
AP.OutStreamer.EmitIntValue(Loc.Offset, 4);
}
}
AP.OutStreamer.AddBlankLine();
CSInfos.clear();
}

View File

@ -626,6 +626,8 @@ void X86AsmPrinter::EmitEndOfAsmFile(Module &M) {
OutStreamer.AddBlankLine();
}
SM.serializeToStackMapSection();
// Funny Darwin hack: This flag tells the linker that no global symbols
// contain code that falls through to other global symbols (e.g. the obvious
// implementation of multiple entry points). If this doesn't occur, the

View File

@ -16,6 +16,7 @@
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/ValueTypes.h"
#include "llvm/CodeGen/StackMaps.h"
#include "llvm/Support/Compiler.h"
namespace llvm {
@ -24,9 +25,20 @@ class MCStreamer;
class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter {
const X86Subtarget *Subtarget;
StackMaps SM;
// Parses operands of PATCHPOINT and STACKMAP to produce stack map Location
// structures. Returns a result location and an iterator to the operand
// immediately following the operands consumed.
//
// This method is implemented in X86MCInstLower.cpp.
static std::pair<StackMaps::Location, MachineInstr::const_mop_iterator>
stackmapOperandParser(MachineInstr::const_mop_iterator MOI,
MachineInstr::const_mop_iterator MOE);
public:
explicit X86AsmPrinter(TargetMachine &TM, MCStreamer &Streamer)
: AsmPrinter(TM, Streamer) {
: AsmPrinter(TM, Streamer), SM(*this, stackmapOperandParser) {
Subtarget = &TM.getSubtarget<X86Subtarget>();
}

View File

@ -24,6 +24,7 @@
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/StackMaps.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/MC/MCAsmInfo.h"
@ -4192,10 +4193,44 @@ breakPartialRegDependency(MachineBasicBlock::iterator MI, unsigned OpNum,
MI->addRegisterKilled(Reg, TRI, true);
}
MachineInstr* X86InstrInfo::foldMemoryOperandImpl(MachineFunction &MF,
MachineInstr *MI,
const SmallVectorImpl<unsigned> &Ops,
int FrameIndex) const {
static MachineInstr* foldPatchpoint(MachineFunction &MF,
MachineInstr *MI,
const SmallVectorImpl<unsigned> &Ops,
int FrameIndex,
const TargetInstrInfo &TII) {
MachineInstr *NewMI =
MF.CreateMachineInstr(TII.get(MI->getOpcode()), MI->getDebugLoc(), true);
MachineInstrBuilder MIB(MF, NewMI);
bool isPatchPoint = MI->getOpcode() == TargetOpcode::PATCHPOINT;
unsigned StartIdx = isPatchPoint ? MI->getOperand(3).getImm() + 4 : 2;
// No need to fold the meta data and function arguments
for (unsigned i = 0; i < StartIdx; ++i)
MIB.addOperand(MI->getOperand(i));
for (unsigned i = StartIdx; i < MI->getNumOperands(); ++i) {
MachineOperand &MO = MI->getOperand(i);
if (std::find(Ops.begin(), Ops.end(), i) != Ops.end()) {
MIB.addOperand(MachineOperand::CreateImm(StackMaps::IndirectMemRefOp));
MIB.addOperand(MachineOperand::CreateFI(FrameIndex));
addOffset(MIB, 0);
}
else
MIB.addOperand(MO);
}
return NewMI;
}
MachineInstr*
X86InstrInfo::foldMemoryOperandImpl(MachineFunction &MF, MachineInstr *MI,
const SmallVectorImpl<unsigned> &Ops,
int FrameIndex) const {
// Special case stack map and patch point intrinsics.
if (MI->getOpcode() == TargetOpcode::STACKMAP
|| MI->getOpcode() == TargetOpcode::PATCHPOINT) {
return foldPatchpoint(MF, MI, Ops, FrameIndex, *this);
}
// Check switch flag
if (NoFusing) return NULL;

View File

@ -17,6 +17,7 @@
#include "X86COFFMachineModuleInfo.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/CodeGen/MachineModuleInfoImpls.h"
#include "llvm/CodeGen/StackMaps.h"
#include "llvm/IR/Type.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
@ -686,6 +687,123 @@ static void LowerTlsAddr(MCStreamer &OutStreamer,
.addExpr(tlsRef));
}
static std::pair<StackMaps::Location, MachineInstr::const_mop_iterator>
parseMemoryOperand(StackMaps::Location::LocationType LocTy,
MachineInstr::const_mop_iterator MOI,
MachineInstr::const_mop_iterator MOE) {
typedef StackMaps::Location Location;
assert(std::distance(MOI, MOE) >= 5 && "Too few operands to encode mem op.");
const MachineOperand &Base = *MOI;
const MachineOperand &Scale = *(++MOI);
const MachineOperand &Index = *(++MOI);
const MachineOperand &Disp = *(++MOI);
const MachineOperand &ZeroReg = *(++MOI);
// Sanity check for supported operand format.
assert(Base.isReg() &&
Scale.isImm() && Scale.getImm() == 1 &&
Index.isReg() && Index.getReg() == 0 &&
Disp.isImm() && ZeroReg.isReg() && (ZeroReg.getReg() == 0) &&
"Unsupported x86 memory operand sequence.");
return std::make_pair(
Location(LocTy, Base.getReg(), Disp.getImm()), ++MOI);
}
std::pair<StackMaps::Location, MachineInstr::const_mop_iterator>
X86AsmPrinter::stackmapOperandParser(MachineInstr::const_mop_iterator MOI,
MachineInstr::const_mop_iterator MOE) {
typedef StackMaps::Location Location;
const MachineOperand &MOP = *MOI;
assert(!MOP.isRegMask() && (!MOP.isReg() || !MOP.isImplicit()) &&
"Register mask and implicit operands should not be processed.");
if (MOP.isImm()) {
switch (MOP.getImm()) {
default: llvm_unreachable("Unrecognized operand type.");
case StackMaps::DirectMemRefOp:
return parseMemoryOperand(StackMaps::Location::Direct,
llvm::next(MOI), MOE);
case StackMaps::IndirectMemRefOp:
return parseMemoryOperand(StackMaps::Location::Indirect,
llvm::next(MOI), MOE);
case StackMaps::ConstantOp: {
++MOI;
assert(MOI->isImm() && "Expected constant operand.");
int64_t Imm = MOI->getImm();
return std::make_pair(Location(Location::Constant, 0, Imm), ++MOI);
}
}
}
// Otherwise this is a reg operand.
assert(MOP.isReg() && "Expected register operand here.");
assert(TargetRegisterInfo::isPhysicalRegister(MOP.getReg()) &&
"Virtreg operands should have been rewritten before now.");
return std::make_pair(Location(Location::Register, MOP.getReg(), 0), ++MOI);
}
static MachineInstr::const_mop_iterator
getStackMapEndMOP(MachineInstr::const_mop_iterator MOI,
MachineInstr::const_mop_iterator MOE) {
for (; MOI != MOE; ++MOI)
if (MOI->isRegMask() || (MOI->isReg() && MOI->isImplicit()))
break;
return MOI;
}
static void LowerSTACKMAP(MCStreamer &OutStreamer,
X86MCInstLower &MCInstLowering,
StackMaps &SM,
const MachineInstr &MI)
{
int64_t ID = MI.getOperand(0).getImm();
unsigned NumNOPBytes = MI.getOperand(1).getImm();
assert((int32_t)ID == ID && "Stack maps hold 32-bit IDs");
SM.recordStackMap(MI, ID, llvm::next(MI.operands_begin(), 2),
getStackMapEndMOP(MI.operands_begin(), MI.operands_end()));
// Emit padding.
for (unsigned i = 0; i < NumNOPBytes; ++i)
OutStreamer.EmitInstruction(MCInstBuilder(X86::NOOP));
}
static void LowerPATCHPOINT(MCStreamer &OutStreamer,
X86MCInstLower &MCInstLowering,
StackMaps &SM,
const MachineInstr &MI)
{
int64_t ID = MI.getOperand(0).getImm();
assert((int32_t)ID == ID && "Stack maps hold 32-bit IDs");
// Get the number of arguments participating in the call. This number was
// adjusted during call lowering by subtracting stack args.
int64_t StackMapIdx = MI.getOperand(3).getImm() + 4;
assert(StackMapIdx <= MI.getNumOperands() && "Patchpoint dropped args.");
SM.recordStackMap(MI, ID, llvm::next(MI.operands_begin(), StackMapIdx),
getStackMapEndMOP(MI.operands_begin(), MI.operands_end()));
// Emit call. We need to know how many bytes we encoded here.
unsigned EncodedBytes = 2;
OutStreamer.EmitInstruction(MCInstBuilder(X86::CALL64r)
.addReg(MI.getOperand(2).getReg()));
// Emit padding.
unsigned NumNOPBytes = MI.getOperand(1).getImm();
assert(NumNOPBytes >= EncodedBytes &&
"Patchpoint can't request size less than the length of a call.");
for (unsigned i = EncodedBytes; i < NumNOPBytes; ++i)
OutStreamer.EmitInstruction(MCInstBuilder(X86::NOOP));
}
void X86AsmPrinter::EmitInstruction(const MachineInstr *MI) {
X86MCInstLower MCInstLowering(*MF, *this);
switch (MI->getOpcode()) {
@ -775,6 +893,12 @@ void X86AsmPrinter::EmitInstruction(const MachineInstr *MI) {
.addExpr(DotExpr));
return;
}
case TargetOpcode::STACKMAP:
return LowerSTACKMAP(OutStreamer, MCInstLowering, SM, *MI);
case TargetOpcode::PATCHPOINT:
return LowerPATCHPOINT(OutStreamer, MCInstLowering, SM, *MI);
}
MCInst TmpInst;

View File

@ -0,0 +1,48 @@
; RUN: llc < %s -march=x86-64 | FileCheck %s
; Trivial patchpoint codegen
;
; FIXME: We should verify that the call target is materialize after
; the label immediately before the call.
; <rdar://15187295> [JS] llvm.webkit.patchpoint call target should be
; materialized in nop slide.
define i64 @trivial_patchpoint_codegen(i64 %p1, i64 %p2, i64 %p3, i64 %p4) {
entry:
; CHECK-LABEL: _trivial_patchpoint_codegen:
; CHECK: Ltmp
; CHECK: callq *%rax
; CHECK-NEXT: nop
; CHECK: movq %rax, %[[REG:r.+]]
; CHECK: callq *%rax
; CHECK-NEXT: nop
; CHECK: movq %[[REG]], %rax
; CHECK: ret
%resolveCall2 = inttoptr i64 -559038736 to i8*
%result = tail call i64 (i32, i32, i8*, i32, ...)* @llvm.experimental.patchpoint.i64(i32 2, i32 12, i8* %resolveCall2, i32 4, i64 %p1, i64 %p2, i64 %p3, i64 %p4)
%resolveCall3 = inttoptr i64 -559038737 to i8*
tail call void (i32, i32, i8*, i32, ...)* @llvm.experimental.patchpoint.void(i32 3, i32 12, i8* %resolveCall3, i32 2, i64 %p1, i64 %result)
ret i64 %result
}
; Caller frame metadata with stackmaps. This should not be optimized
; as a leaf function.
;
; CHECK-LABEL: _caller_meta_leaf
; CHECK: subq $24, %rsp
; CHECK: Ltmp
; CHECK: addq $24, %rsp
; CHECK: ret
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
call void (i32, i32, ...)* @llvm.experimental.stackmap(i32 4, i32 0, i64* %metadata)
ret void
}
declare void @llvm.experimental.stackmap(i32, i32, ...)
declare void @llvm.experimental.patchpoint.void(i32, i32, i8*, i32, ...)
declare i64 @llvm.experimental.patchpoint.i64(i32, i32, i8*, i32, ...)

View File

@ -0,0 +1,205 @@
; RUN: llc < %s -march=x86-64 | FileCheck %s
;
; Note: Print verbose stackmaps using -debug-only=stackmaps.
; CHECK-LABEL: .section __LLVM_STACKMAPS,__llvm_stackmaps
; CHECK-NEXT: __LLVM_StackMaps:
; CHECK-NEXT: .long 0
; Num LargeConstants
; CHECK-NEXT: .long 1
; CHECK-NEXT: .quad 4294967296
; Num Callsites
; CHECK-NEXT: .long 8
; Constant arguments
;
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long L{{.*}}-_constantargs
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 4
; SmallConstant
; CHECK-NEXT: .byte 4
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short 0
; CHECK-NEXT: .long 65535
; SmallConstant
; CHECK-NEXT: .byte 4
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short 0
; CHECK-NEXT: .long 65536
; SmallConstant
; CHECK-NEXT: .byte 4
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short 0
; CHECK-NEXT: .long 4294967295
; LargeConstant at index 0
; CHECK-NEXT: .byte 5
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short 0
; CHECK-NEXT: .long 0
define void @constantargs() {
entry:
%0 = inttoptr i64 12345 to i8*
tail call void (i32, i32, i8*, i32, ...)* @llvm.experimental.patchpoint.void(i32 1, i32 2, i8* %0, i32 0, i64 65535, i64 65536, i64 4294967295, i64 4294967296)
ret void
}
; Inline OSR Exit
;
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long L{{.*}}-_osrinline
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 2
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short {{[0-9]+}}
; CHECK-NEXT: .long 0
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short {{[0-9]+}}
; CHECK-NEXT: .long 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 (i32, i32, ...)* @llvm.experimental.stackmap(i32 3, i32 12, i64 %a, i64 %b)
ret void
}
; Cold OSR Exit
;
; 2 live variables in register.
;
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long L{{.*}}-_osrcold
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 2
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short {{[0-9]+}}
; CHECK-NEXT: .long 0
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short {{[0-9]+}}
; CHECK-NEXT: .long 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 -559038737 to i8*
call void (i32, i32, i8*, i32, ...)* @llvm.experimental.patchpoint.void(i32 4, i32 12, i8* %thunk, i32 0, i64 %a, i64 %b)
unreachable
ret:
ret void
}
; Property Read
; CHECK-NEXT: .long 5
; CHECK-NEXT: .long L{{.*}}-_propertyRead
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 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 -559038737 to i8*
%result = call i64 (i32, i32, i8*, i32, ...)* @llvm.experimental.patchpoint.i64(i32 5, i32 12, i8* %resolveRead, i32 1, i64* %obj)
%add = add i64 %result, 3
ret i64 %add
}
; Property Write
; CHECK-NEXT: .long 6
; CHECK-NEXT: .long L{{.*}}-_propertyWrite
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 0
;
; FIXME: There are currently no stackmap entries. After moving to
; AnyRegCC, we will have entries for the object and return value.
define void @propertyWrite(i64 %dummy1, i64* %obj, i64 %dummy2, i64 %a) {
entry:
%resolveWrite = inttoptr i64 -559038737 to i8*
call void (i32, i32, i8*, i32, ...)* @llvm.experimental.patchpoint.void(i32 6, i32 12, i8* %resolveWrite, i32 2, i64* %obj, i64 %a)
ret void
}
; Void JS Call
;
; 2 live variables in registers.
;
; CHECK-NEXT: .long 7
; CHECK-NEXT: .long L{{.*}}-_jsVoidCall
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 2
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short {{[0-9]+}}
; CHECK-NEXT: .long 0
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short {{[0-9]+}}
; CHECK-NEXT: .long 0
define void @jsVoidCall(i64 %dummy1, i64* %obj, i64 %arg, i64 %l1, i64 %l2) {
entry:
%resolveCall = inttoptr i64 -559038737 to i8*
call void (i32, i32, i8*, i32, ...)* @llvm.experimental.patchpoint.void(i32 7, i32 12, i8* %resolveCall, i32 2, i64* %obj, i64 %arg, i64 %l1, i64 %l2)
ret void
}
; i64 JS Call
;
; 2 live variables in registers.
;
; CHECK: .long 8
; CHECK-NEXT: .long L{{.*}}-_jsIntCall
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 2
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short {{[0-9]+}}
; CHECK-NEXT: .long 0
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 0
; CHECK-NEXT: .short {{[0-9]+}}
; CHECK-NEXT: .long 0
define i64 @jsIntCall(i64 %dummy1, i64* %obj, i64 %arg, i64 %l1, i64 %l2) {
entry:
%resolveCall = inttoptr i64 -559038737 to i8*
%result = call i64 (i32, i32, i8*, i32, ...)* @llvm.experimental.patchpoint.i64(i32 8, i32 12, 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 17 stack map entries.
;
; CHECK: .long 11
; CHECK-NEXT: .long L{{.*}}-_spilledValue
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 10
;
; Check that at least one is a spilled entry (Indirect).
; CHECK: .byte 3
; CHECK: .byte 0
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) {
entry:
%resolveCall = inttoptr i64 -559038737 to i8*
call void (i32, i32, i8*, i32, ...)* @llvm.experimental.patchpoint.void(i32 11, i32 12, i8* %resolveCall, 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)
; FIXME: The Spiller needs to be able to fold all rematted loads! This
; can be seen by adding %l15 to the stackmap.
; <rdar:/15202984> [JS] Ran out of registers during register allocation
; %resolveCall = inttoptr i64 -559038737 to i8*
; call void (i32, i32, i8*, i32, ...)* @llvm.experimental.patchpoint.void(i32 12, i32 12, i8* %resolveCall, 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)
ret void
}
declare void @llvm.experimental.stackmap(i32, i32, ...)
declare void @llvm.experimental.patchpoint.void(i32, i32, i8*, i32, ...)
declare i64 @llvm.experimental.patchpoint.i64(i32, i32, i8*, i32, ...)