mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-25 21:00:00 +00:00
HHVM calling conventions.
HHVM calling convention, hhvmcc, is used by HHVM JIT for functions in translated cache. We currently support LLVM back end to generate code for X86-64 and may support other architectures in the future. In HHVM calling convention any GP register could be used to pass and return values, with the exception of R12 which is reserved for thread-local area and is callee-saved. Other than R12, we always pass RBX and RBP as args, which are our virtual machine's stack pointer and frame pointer respectively. When we enter translation cache via hhvmcc function, we expect the stack to be aligned at 16 bytes, i.e. skewed by 8 bytes as opposed to standard ABI alignment. This affects stack object alignment and stack adjustments for function calls. One extra calling convention, hhvm_ccc, is used to call C++ helpers from HHVM's translation cache. It is almost identical to standard C calling convention with an exception of first argument which is passed in RBP (before we use RDI, RSI, etc.) Differential Revision: http://reviews.llvm.org/D12681 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@248832 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
5518a74f1f
commit
3b3752c898
@ -147,7 +147,16 @@ namespace CallingConv {
|
||||
|
||||
/// \brief MSVC calling convention that passes vectors and vector aggregates
|
||||
/// in SSE registers.
|
||||
X86_VectorCall = 80
|
||||
X86_VectorCall = 80,
|
||||
|
||||
/// \brief Calling convention used by HipHop Virtual Machine (HHVM) to
|
||||
/// perform calls to and from translation cache, and for calling PHP
|
||||
/// functions.
|
||||
/// HHVM calling convention supports tail/sibling call elimination.
|
||||
HHVM = 81,
|
||||
|
||||
/// \brief HHVM calling convention for invoking C/C++ helpers.
|
||||
HHVM_C = 82
|
||||
};
|
||||
} // End CallingConv namespace
|
||||
|
||||
|
@ -599,15 +599,27 @@ inline uint64_t PowerOf2Floor(uint64_t A) {
|
||||
/// Returns the next integer (mod 2**64) that is greater than or equal to
|
||||
/// \p Value and is a multiple of \p Align. \p Align must be non-zero.
|
||||
///
|
||||
/// If non-zero \p Skew is specified, the return value will be a minimal
|
||||
/// integer that is greater than or equal to \p Value and equal to
|
||||
/// \p Align * N + \p Skew for some integer N. If \p Skew is larger than
|
||||
/// \p Align, its value is adjusted to '\p Skew mod \p Align'.
|
||||
///
|
||||
/// Examples:
|
||||
/// \code
|
||||
/// RoundUpToAlignment(5, 8) = 8
|
||||
/// RoundUpToAlignment(17, 8) = 24
|
||||
/// RoundUpToAlignment(~0LL, 8) = 0
|
||||
/// RoundUpToAlignment(321, 255) = 510
|
||||
///
|
||||
/// RoundUpToAlignment(5, 8, 7) = 7
|
||||
/// RoundUpToAlignment(17, 8, 1) = 17
|
||||
/// RoundUpToAlignment(~0LL, 8, 3) = 3
|
||||
/// RoundUpToAlignment(321, 255, 42) = 552
|
||||
/// \endcode
|
||||
inline uint64_t RoundUpToAlignment(uint64_t Value, uint64_t Align) {
|
||||
return (Value + Align - 1) / Align * Align;
|
||||
inline uint64_t RoundUpToAlignment(uint64_t Value, uint64_t Align,
|
||||
uint64_t Skew = 0) {
|
||||
Skew %= Align;
|
||||
return (Value + Align - 1 - Skew) / Align * Align + Skew;
|
||||
}
|
||||
|
||||
/// Returns the offset to the next integer (mod 2**64) that is greater than
|
||||
|
@ -96,6 +96,11 @@ public:
|
||||
return StackRealignable;
|
||||
}
|
||||
|
||||
/// Return the skew that has to be applied to stack alignment under
|
||||
/// certain conditions (e.g. stack was adjusted before function \p MF
|
||||
/// was called).
|
||||
virtual unsigned getStackAlignmentSkew(const MachineFunction &MF) const;
|
||||
|
||||
/// getOffsetOfLocalArea - This method returns the offset of the local area
|
||||
/// from the stack pointer on entrance to a function.
|
||||
///
|
||||
|
@ -587,6 +587,8 @@ lltok::Kind LLLexer::LexIdentifier() {
|
||||
KEYWORD(preserve_mostcc);
|
||||
KEYWORD(preserve_allcc);
|
||||
KEYWORD(ghccc);
|
||||
KEYWORD(hhvmcc);
|
||||
KEYWORD(hhvm_ccc);
|
||||
|
||||
KEYWORD(cc);
|
||||
KEYWORD(c);
|
||||
|
@ -1532,6 +1532,8 @@ bool LLParser::ParseOptionalDLLStorageClass(unsigned &Res) {
|
||||
/// ::= 'preserve_mostcc'
|
||||
/// ::= 'preserve_allcc'
|
||||
/// ::= 'ghccc'
|
||||
/// ::= 'hhvmcc'
|
||||
/// ::= 'hhvm_ccc'
|
||||
/// ::= 'cc' UINT
|
||||
///
|
||||
bool LLParser::ParseOptionalCallingConv(unsigned &CC) {
|
||||
@ -1560,6 +1562,8 @@ bool LLParser::ParseOptionalCallingConv(unsigned &CC) {
|
||||
case lltok::kw_preserve_mostcc:CC = CallingConv::PreserveMost; break;
|
||||
case lltok::kw_preserve_allcc: CC = CallingConv::PreserveAll; break;
|
||||
case lltok::kw_ghccc: CC = CallingConv::GHC; break;
|
||||
case lltok::kw_hhvmcc: CC = CallingConv::HHVM; break;
|
||||
case lltok::kw_hhvm_ccc: CC = CallingConv::HHVM_C; break;
|
||||
case lltok::kw_cc: {
|
||||
Lex.Lex();
|
||||
return ParseUInt32(CC);
|
||||
|
@ -97,6 +97,7 @@ namespace lltok {
|
||||
kw_webkit_jscc, kw_anyregcc,
|
||||
kw_preserve_mostcc, kw_preserve_allcc,
|
||||
kw_ghccc,
|
||||
kw_hhvmcc, kw_hhvm_ccc,
|
||||
|
||||
// Attributes:
|
||||
kw_attributes,
|
||||
|
@ -500,7 +500,7 @@ void PEI::insertCSRSpillsAndRestores(MachineFunction &Fn) {
|
||||
static inline void
|
||||
AdjustStackOffset(MachineFrameInfo *MFI, int FrameIdx,
|
||||
bool StackGrowsDown, int64_t &Offset,
|
||||
unsigned &MaxAlign) {
|
||||
unsigned &MaxAlign, unsigned Skew) {
|
||||
// If the stack grows down, add the object size to find the lowest address.
|
||||
if (StackGrowsDown)
|
||||
Offset += MFI->getObjectSize(FrameIdx);
|
||||
@ -512,7 +512,7 @@ AdjustStackOffset(MachineFrameInfo *MFI, int FrameIdx,
|
||||
MaxAlign = std::max(MaxAlign, Align);
|
||||
|
||||
// Adjust to alignment boundary.
|
||||
Offset = RoundUpToAlignment(Offset, Align);
|
||||
Offset = RoundUpToAlignment(Offset, Align, Skew);
|
||||
|
||||
if (StackGrowsDown) {
|
||||
DEBUG(dbgs() << "alloc FI(" << FrameIdx << ") at SP[" << -Offset << "]\n");
|
||||
@ -530,12 +530,12 @@ static void
|
||||
AssignProtectedObjSet(const StackObjSet &UnassignedObjs,
|
||||
SmallSet<int, 16> &ProtectedObjs,
|
||||
MachineFrameInfo *MFI, bool StackGrowsDown,
|
||||
int64_t &Offset, unsigned &MaxAlign) {
|
||||
int64_t &Offset, unsigned &MaxAlign, unsigned Skew) {
|
||||
|
||||
for (StackObjSet::const_iterator I = UnassignedObjs.begin(),
|
||||
E = UnassignedObjs.end(); I != E; ++I) {
|
||||
int i = *I;
|
||||
AdjustStackOffset(MFI, i, StackGrowsDown, Offset, MaxAlign);
|
||||
AdjustStackOffset(MFI, i, StackGrowsDown, Offset, MaxAlign, Skew);
|
||||
ProtectedObjs.insert(i);
|
||||
}
|
||||
}
|
||||
@ -563,6 +563,9 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &Fn) {
|
||||
&& "Local area offset should be in direction of stack growth");
|
||||
int64_t Offset = LocalAreaOffset;
|
||||
|
||||
// Skew to be applied to alignment.
|
||||
unsigned Skew = TFI.getStackAlignmentSkew(Fn);
|
||||
|
||||
// If there are fixed sized objects that are preallocated in the local area,
|
||||
// non-fixed objects can't be allocated right at the start of local area.
|
||||
// We currently don't support filling in holes in between fixed sized
|
||||
@ -593,7 +596,7 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &Fn) {
|
||||
|
||||
unsigned Align = MFI->getObjectAlignment(i);
|
||||
// Adjust to alignment boundary
|
||||
Offset = RoundUpToAlignment(Offset, Align);
|
||||
Offset = RoundUpToAlignment(Offset, Align, Skew);
|
||||
|
||||
MFI->setObjectOffset(i, -Offset); // Set the computed offset
|
||||
}
|
||||
@ -602,7 +605,7 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &Fn) {
|
||||
for (int i = MaxCSFI; i >= MinCSFI ; --i) {
|
||||
unsigned Align = MFI->getObjectAlignment(i);
|
||||
// Adjust to alignment boundary
|
||||
Offset = RoundUpToAlignment(Offset, Align);
|
||||
Offset = RoundUpToAlignment(Offset, Align, Skew);
|
||||
|
||||
MFI->setObjectOffset(i, Offset);
|
||||
Offset += MFI->getObjectSize(i);
|
||||
@ -624,7 +627,7 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &Fn) {
|
||||
RS->getScavengingFrameIndices(SFIs);
|
||||
for (SmallVectorImpl<int>::iterator I = SFIs.begin(),
|
||||
IE = SFIs.end(); I != IE; ++I)
|
||||
AdjustStackOffset(MFI, *I, StackGrowsDown, Offset, MaxAlign);
|
||||
AdjustStackOffset(MFI, *I, StackGrowsDown, Offset, MaxAlign, Skew);
|
||||
}
|
||||
|
||||
// FIXME: Once this is working, then enable flag will change to a target
|
||||
@ -635,7 +638,7 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &Fn) {
|
||||
unsigned Align = MFI->getLocalFrameMaxAlign();
|
||||
|
||||
// Adjust to alignment boundary.
|
||||
Offset = RoundUpToAlignment(Offset, Align);
|
||||
Offset = RoundUpToAlignment(Offset, Align, Skew);
|
||||
|
||||
DEBUG(dbgs() << "Local frame base offset: " << Offset << "\n");
|
||||
|
||||
@ -662,7 +665,7 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &Fn) {
|
||||
StackObjSet AddrOfObjs;
|
||||
|
||||
AdjustStackOffset(MFI, MFI->getStackProtectorIndex(), StackGrowsDown,
|
||||
Offset, MaxAlign);
|
||||
Offset, MaxAlign, Skew);
|
||||
|
||||
// Assign large stack objects first.
|
||||
for (unsigned i = 0, e = MFI->getObjectIndexEnd(); i != e; ++i) {
|
||||
@ -695,11 +698,11 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &Fn) {
|
||||
}
|
||||
|
||||
AssignProtectedObjSet(LargeArrayObjs, ProtectedObjs, MFI, StackGrowsDown,
|
||||
Offset, MaxAlign);
|
||||
Offset, MaxAlign, Skew);
|
||||
AssignProtectedObjSet(SmallArrayObjs, ProtectedObjs, MFI, StackGrowsDown,
|
||||
Offset, MaxAlign);
|
||||
Offset, MaxAlign, Skew);
|
||||
AssignProtectedObjSet(AddrOfObjs, ProtectedObjs, MFI, StackGrowsDown,
|
||||
Offset, MaxAlign);
|
||||
Offset, MaxAlign, Skew);
|
||||
}
|
||||
|
||||
// Then assign frame offsets to stack objects that are not used to spill
|
||||
@ -719,7 +722,7 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &Fn) {
|
||||
if (ProtectedObjs.count(i))
|
||||
continue;
|
||||
|
||||
AdjustStackOffset(MFI, i, StackGrowsDown, Offset, MaxAlign);
|
||||
AdjustStackOffset(MFI, i, StackGrowsDown, Offset, MaxAlign, Skew);
|
||||
}
|
||||
|
||||
// Make sure the special register scavenging spill slot is closest to the
|
||||
@ -729,7 +732,7 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &Fn) {
|
||||
RS->getScavengingFrameIndices(SFIs);
|
||||
for (SmallVectorImpl<int>::iterator I = SFIs.begin(),
|
||||
IE = SFIs.end(); I != IE; ++I)
|
||||
AdjustStackOffset(MFI, *I, StackGrowsDown, Offset, MaxAlign);
|
||||
AdjustStackOffset(MFI, *I, StackGrowsDown, Offset, MaxAlign, Skew);
|
||||
}
|
||||
|
||||
if (!TFI.targetHandlesStackFrameRounding()) {
|
||||
@ -754,7 +757,7 @@ void PEI::calculateFrameObjectOffsets(MachineFunction &Fn) {
|
||||
// If the frame pointer is eliminated, all frame offsets will be relative to
|
||||
// SP not FP. Align to MaxAlign so this works.
|
||||
StackAlign = std::max(StackAlign, MaxAlign);
|
||||
Offset = RoundUpToAlignment(Offset, StackAlign);
|
||||
Offset = RoundUpToAlignment(Offset, StackAlign, Skew);
|
||||
}
|
||||
|
||||
// Update frame info to pretend that this is part of the stack...
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
#include "llvm/CodeGen/MachineModuleInfo.h"
|
||||
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
||||
#include "llvm/IR/CallingConv.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Target/TargetRegisterInfo.h"
|
||||
#include "llvm/Target/TargetSubtargetInfo.h"
|
||||
@ -81,3 +82,13 @@ void TargetFrameLowering::determineCalleeSaves(MachineFunction &MF,
|
||||
SavedRegs.set(Reg);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned TargetFrameLowering::getStackAlignmentSkew(
|
||||
const MachineFunction &MF) const {
|
||||
// When HHVM function is called, the stack is skewed as the return address
|
||||
// is removed from the stack before we enter the function.
|
||||
if (LLVM_UNLIKELY(MF.getFunction()->getCallingConv() == CallingConv::HHVM))
|
||||
return MF.getTarget().getPointerSize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -319,6 +319,8 @@ static void PrintCallingConv(unsigned cc, raw_ostream &Out) {
|
||||
case CallingConv::X86_64_Win64: Out << "x86_64_win64cc"; break;
|
||||
case CallingConv::SPIR_FUNC: Out << "spir_func"; break;
|
||||
case CallingConv::SPIR_KERNEL: Out << "spir_kernel"; break;
|
||||
case CallingConv::HHVM: Out << "hhvmcc"; break;
|
||||
case CallingConv::HHVM_C: Out << "hhvm_ccc"; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,6 +202,16 @@ def RetCC_X86_64_AnyReg : CallingConv<[
|
||||
CCCustom<"CC_X86_AnyReg_Error">
|
||||
]>;
|
||||
|
||||
// X86-64 HHVM return-value convention.
|
||||
def RetCC_X86_64_HHVM: CallingConv<[
|
||||
// Promote all types to i64
|
||||
CCIfType<[i8, i16, i32], CCPromoteToType<i64>>,
|
||||
|
||||
// Return: could return in any GP register save RSP and R12.
|
||||
CCIfType<[i64], CCAssignToReg<[RBX, RBP, RDI, RSI, RDX, RCX, R8, R9,
|
||||
RAX, R10, R11, R13, R14, R15]>>
|
||||
]>;
|
||||
|
||||
// This is the root return-value convention for the X86-32 backend.
|
||||
def RetCC_X86_32 : CallingConv<[
|
||||
// If FastCC, use RetCC_X86_32_Fast.
|
||||
@ -227,6 +237,9 @@ def RetCC_X86_64 : CallingConv<[
|
||||
CCIfCC<"CallingConv::X86_64_Win64", CCDelegateTo<RetCC_X86_Win64_C>>,
|
||||
CCIfCC<"CallingConv::X86_64_SysV", CCDelegateTo<RetCC_X86_64_C>>,
|
||||
|
||||
// Handle HHVM calls.
|
||||
CCIfCC<"CallingConv::HHVM", CCDelegateTo<RetCC_X86_64_HHVM>>,
|
||||
|
||||
// Mingw64 and native Win64 use Win64 CC
|
||||
CCIfSubtarget<"isTargetWin64()", CCDelegateTo<RetCC_X86_Win64_C>>,
|
||||
|
||||
@ -319,6 +332,23 @@ def CC_X86_64_C : CallingConv<[
|
||||
CCAssignToStack<64, 64>>
|
||||
]>;
|
||||
|
||||
// Calling convention for X86-64 HHVM.
|
||||
def CC_X86_64_HHVM : CallingConv<[
|
||||
// Use all/any GP registers for args, except RSP.
|
||||
CCIfType<[i64], CCAssignToReg<[RBX, R12, RBP, R15,
|
||||
RDI, RSI, RDX, RCX, R8, R9,
|
||||
RAX, R10, R11, R13, R14]>>
|
||||
]>;
|
||||
|
||||
// Calling convention for helper functions in HHVM.
|
||||
def CC_X86_64_HHVM_C : CallingConv<[
|
||||
// Pass the first argument in RBP.
|
||||
CCIfType<[i64], CCAssignToReg<[RBP]>>,
|
||||
|
||||
// Otherwise it's the same as the regular C calling convention.
|
||||
CCDelegateTo<CC_X86_64_C>
|
||||
]>;
|
||||
|
||||
// Calling convention used on Win64
|
||||
def CC_X86_Win64_C : CallingConv<[
|
||||
// FIXME: Handle byval stuff.
|
||||
@ -734,6 +764,8 @@ def CC_X86_64 : CallingConv<[
|
||||
CCIfCC<"CallingConv::X86_64_Win64", CCDelegateTo<CC_X86_Win64_C>>,
|
||||
CCIfCC<"CallingConv::X86_64_SysV", CCDelegateTo<CC_X86_64_C>>,
|
||||
CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<CC_X86_Win64_VectorCall>>,
|
||||
CCIfCC<"CallingConv::HHVM", CCDelegateTo<CC_X86_64_HHVM>>,
|
||||
CCIfCC<"CallingConv::HHVM_C", CCDelegateTo<CC_X86_64_HHVM_C>>,
|
||||
|
||||
// Mingw64 and native Win64 use Win64 CC
|
||||
CCIfSubtarget<"isTargetWin64()", CCDelegateTo<CC_X86_Win64_C>>,
|
||||
@ -804,3 +836,6 @@ def CSR_64_Intel_OCL_BI_AVX : CalleeSavedRegs<(add CSR_64,
|
||||
def CSR_64_Intel_OCL_BI_AVX512 : CalleeSavedRegs<(add RBX, RDI, RSI, R14, R15,
|
||||
(sequence "ZMM%u", 16, 31),
|
||||
K4, K5, K6, K7)>;
|
||||
|
||||
// Only R12 is preserved for PHP calls in HHVM.
|
||||
def CSR_64_HHVM : CalleeSavedRegs<(add R12)>;
|
||||
|
@ -2426,7 +2426,7 @@ CreateCopyOfByValArgument(SDValue Src, SDValue Dst, SDValue Chain,
|
||||
/// supports tail call optimization.
|
||||
static bool IsTailCallConvention(CallingConv::ID CC) {
|
||||
return (CC == CallingConv::Fast || CC == CallingConv::GHC ||
|
||||
CC == CallingConv::HiPE);
|
||||
CC == CallingConv::HiPE || CC == CallingConv::HHVM);
|
||||
}
|
||||
|
||||
/// \brief Return true if the calling convention is a C calling convention.
|
||||
@ -3900,6 +3900,10 @@ bool X86::isOffsetSuitableForCodeModel(int64_t Offset, CodeModel::Model M,
|
||||
/// Callee pop is necessary to support tail calls.
|
||||
bool X86::isCalleePop(CallingConv::ID CallingConv,
|
||||
bool is64Bit, bool IsVarArg, bool TailCallOpt) {
|
||||
|
||||
if (IsTailCallConvention(CallingConv))
|
||||
return IsVarArg ? false : TailCallOpt;
|
||||
|
||||
switch (CallingConv) {
|
||||
default:
|
||||
return false;
|
||||
@ -3907,12 +3911,6 @@ bool X86::isCalleePop(CallingConv::ID CallingConv,
|
||||
case CallingConv::X86_FastCall:
|
||||
case CallingConv::X86_ThisCall:
|
||||
return !is64Bit;
|
||||
case CallingConv::Fast:
|
||||
case CallingConv::GHC:
|
||||
case CallingConv::HiPE:
|
||||
if (IsVarArg)
|
||||
return false;
|
||||
return TailCallOpt;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,6 +256,8 @@ X86RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
|
||||
return CSR_64_Intel_OCL_BI_SaveList;
|
||||
break;
|
||||
}
|
||||
case CallingConv::HHVM:
|
||||
return CSR_64_HHVM_SaveList;
|
||||
case CallingConv::Cold:
|
||||
if (Is64Bit)
|
||||
return CSR_64_MostRegs_SaveList;
|
||||
@ -316,6 +318,8 @@ X86RegisterInfo::getCallPreservedMask(const MachineFunction &MF,
|
||||
return CSR_64_Intel_OCL_BI_RegMask;
|
||||
break;
|
||||
}
|
||||
case CallingConv::HHVM:
|
||||
return CSR_64_HHVM_RegMask;
|
||||
case CallingConv::Cold:
|
||||
if (Is64Bit)
|
||||
return CSR_64_MostRegs_RegMask;
|
||||
|
241
test/CodeGen/X86/hhvm-cc.ll
Normal file
241
test/CodeGen/X86/hhvm-cc.ll
Normal file
@ -0,0 +1,241 @@
|
||||
; RUN: llc < %s | FileCheck %s
|
||||
|
||||
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
declare hhvmcc i64 @bar(i64, i64, i64) nounwind
|
||||
|
||||
; Simply check we can modify %rbx and %rbp before returning via call to bar.
|
||||
define hhvmcc i64 @foo(i64 %a, i64 %b, i64 %c) nounwind {
|
||||
entry:
|
||||
; CHECK-LABEL: foo:
|
||||
; CHECK-DAG: movl $1, %ebx
|
||||
; CHECK-DAG: movl $3, %ebp
|
||||
; CHECK: jmp bar
|
||||
%ret = musttail call hhvmcc i64 @bar(i64 1, i64 %b, i64 3)
|
||||
ret i64 %ret
|
||||
}
|
||||
|
||||
; Check that we can read and modify %rbx returned from PHP function.
|
||||
define hhvmcc i64 @mod_return(i64 %a, i64 %b, i64 %c) nounwind {
|
||||
entry:
|
||||
; CHECK-LABEL: mod_return:
|
||||
; CHECK-NEXT: {{^#.*}}
|
||||
; CHECK-NEXT: callq bar
|
||||
; CHECK-NEXT: incq %rbx
|
||||
%tmp = call hhvmcc i64 @bar(i64 %a, i64 %b, i64 %c)
|
||||
%retval = add i64 %tmp, 1
|
||||
ret i64 %retval
|
||||
}
|
||||
|
||||
%rettype = type { i64, i64, i64, i64, i64, i64, i64,
|
||||
i64, i64, i64, i64, i64, i64, i64
|
||||
}
|
||||
|
||||
; Check that we can return up to 14 64-bit args in registers.
|
||||
define hhvmcc %rettype @return_all(i64 %a, i64 %b, i64 %c) nounwind {
|
||||
entry:
|
||||
; CHECK-LABEL: return_all:
|
||||
; CHECK-DAG: movl $1, %ebx
|
||||
; CHECK-DAG: movl $2, %ebp
|
||||
; CHECK-DAG: movl $3, %edi
|
||||
; CHECK-DAG: movl $4, %esi
|
||||
; CHECK-DAG: movl $5, %edx
|
||||
; CHECK-DAG: movl $6, %ecx
|
||||
; CHECK-DAG: movl $7, %r8
|
||||
; CHECK-DAG: movl $8, %r9
|
||||
; CHECK-DAG: movl $9, %eax
|
||||
; CHECK-DAG: movl $10, %r10
|
||||
; CHECK-DAG: movl $11, %r11
|
||||
; CHECK-DAG: movl $12, %r13
|
||||
; CHECK-DAG: movl $13, %r14
|
||||
; CHECK-DAG: movl $14, %r15
|
||||
; CHECK: retq
|
||||
%r1 = insertvalue %rettype zeroinitializer, i64 1, 0
|
||||
%r2 = insertvalue %rettype %r1, i64 2, 1
|
||||
%r3 = insertvalue %rettype %r2, i64 3, 2
|
||||
%r4 = insertvalue %rettype %r3, i64 4, 3
|
||||
%r5 = insertvalue %rettype %r4, i64 5, 4
|
||||
%r6 = insertvalue %rettype %r5, i64 6, 5
|
||||
%r7 = insertvalue %rettype %r6, i64 7, 6
|
||||
%r8 = insertvalue %rettype %r7, i64 8, 7
|
||||
%r9 = insertvalue %rettype %r8, i64 9, 8
|
||||
%r10 = insertvalue %rettype %r9, i64 10, 9
|
||||
%r11 = insertvalue %rettype %r10, i64 11, 10
|
||||
%r12 = insertvalue %rettype %r11, i64 12, 11
|
||||
%r13 = insertvalue %rettype %r12, i64 13, 12
|
||||
%r14 = insertvalue %rettype %r13, i64 14, 13
|
||||
ret %rettype %r14
|
||||
}
|
||||
|
||||
declare hhvmcc void @return_all_tc(i64, i64, i64, i64, i64, i64, i64, i64,
|
||||
i64, i64, i64, i64, i64, i64, i64)
|
||||
|
||||
; Check that we can return up to 14 64-bit args in registers via tail call.
|
||||
define hhvmcc void @test_return_all_tc(i64 %a, i64 %b, i64 %c) nounwind {
|
||||
entry:
|
||||
; CHECK-LABEL: test_return_all_tc:
|
||||
; CHECK-NEXT: {{^#.*}}
|
||||
; CHECK-DAG: movl $1, %ebx
|
||||
; CHECK-DAG: movl $3, %ebp
|
||||
; CHECK-DAG: movl $4, %r15
|
||||
; CHECK-DAG: movl $5, %edi
|
||||
; CHECK-DAG: movl $6, %esi
|
||||
; CHECK-DAG: movl $7, %edx
|
||||
; CHECK-DAG: movl $8, %ecx
|
||||
; CHECK-DAG: movl $9, %r8
|
||||
; CHECK-DAG: movl $10, %r9
|
||||
; CHECK-DAG: movl $11, %eax
|
||||
; CHECK-DAG: movl $12, %r10
|
||||
; CHECK-DAG: movl $13, %r11
|
||||
; CHECK-DAG: movl $14, %r13
|
||||
; CHECK-DAG: movl $15, %r14
|
||||
; CHECK: jmp return_all_tc
|
||||
tail call hhvmcc void @return_all_tc(
|
||||
i64 1, i64 %b, i64 3, i64 4, i64 5, i64 6, i64 7,
|
||||
i64 8, i64 9, i64 10, i64 11, i64 12, i64 13, i64 14, i64 15)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare hhvmcc {i64, i64} @php_short(i64, i64, i64, i64)
|
||||
|
||||
define hhvmcc i64 @test_php_short(i64 %a, i64 %b, i64 %c) nounwind {
|
||||
entry:
|
||||
; CHECK-LABEL: test_php_short:
|
||||
; CHECK-NEXT: {{^#.*}}
|
||||
; CHECK-NEXT: movl $42, %r15
|
||||
; CHECK-NEXT: callq php_short
|
||||
; CHECK-NEXT: leaq (%rbp,%r12), %rbx
|
||||
; CHECK-NEXT: retq
|
||||
%pair = call hhvmcc {i64, i64} @php_short(i64 %a, i64 %b, i64 %c, i64 42)
|
||||
%fp = extractvalue {i64, i64} %pair, 1
|
||||
%rv = add i64 %fp, %b
|
||||
ret i64 %rv
|
||||
}
|
||||
|
||||
declare hhvmcc %rettype @php_all(i64, i64, i64, i64, i64, i64, i64,
|
||||
i64, i64, i64, i64, i64, i64, i64, i64)
|
||||
|
||||
; Check that we can pass 15 arguments in registers.
|
||||
; Also check that %r12 (2nd arg) is not spilled.
|
||||
define hhvmcc i64 @test_php_all(i64 %a, i64 %b, i64 %c) nounwind {
|
||||
entry:
|
||||
; CHECK-LABEL: test_php_all:
|
||||
; CHECK-NEXT: {{^#.*}}
|
||||
; CHECK-NOT: sub
|
||||
; CHECK-NOT: sub
|
||||
; CHECK-DAG: movl $1, %ebx
|
||||
; CHECK-DAG: movl $3, %ebp
|
||||
; CHECK-DAG: movl $4, %r15
|
||||
; CHECK-DAG: movl $5, %edi
|
||||
; CHECK-DAG: movl $6, %esi
|
||||
; CHECK-DAG: movl $7, %edx
|
||||
; CHECK-DAG: movl $8, %ecx
|
||||
; CHECK-DAG: movl $9, %r8
|
||||
; CHECK-DAG: movl $10, %r9
|
||||
; CHECK-DAG: movl $11, %eax
|
||||
; CHECK-DAG: movl $12, %r10
|
||||
; CHECK-DAG: movl $13, %r11
|
||||
; CHECK-DAG: movl $14, %r13
|
||||
; CHECK-DAG: movl $15, %r14
|
||||
; CHECK: callq php_all
|
||||
%pair = call hhvmcc %rettype @php_all(
|
||||
i64 1, i64 %b, i64 3, i64 4, i64 5, i64 6, i64 7,
|
||||
i64 8, i64 9, i64 10, i64 11, i64 12, i64 13, i64 14, i64 15)
|
||||
%fp = extractvalue %rettype %pair, 1
|
||||
%rv = add i64 %fp, %b
|
||||
ret i64 %rv
|
||||
}
|
||||
|
||||
declare hhvmcc void @svcreq(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64,
|
||||
i64, i64)
|
||||
|
||||
define hhvmcc void @test_svcreq(i64 %a, i64 %b, i64 %c) nounwind {
|
||||
entry:
|
||||
; CHECK-LABEL: test_svcreq:
|
||||
; CHECK-DAG: movl $42, %r10
|
||||
; CHECK-DAG: movl $1, %edi
|
||||
; CHECK-DAG: movl $2, %esi
|
||||
; CHECK-DAG: movl $3, %edx
|
||||
; CHECK-DAG: movl $4, %ecx
|
||||
; CHECK-DAG: movl $5, %r8
|
||||
; CHECK-DAG: movl $6, %r9
|
||||
; CHECK: jmp svcreq
|
||||
tail call hhvmcc void @svcreq(i64 %a, i64 %b, i64 %c, i64 undef, i64 1,
|
||||
i64 2, i64 3, i64 4, i64 5, i64 6, i64 undef,
|
||||
i64 42)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare hhvm_ccc void @helper_short(i64, i64, i64, i64, i64, i64, i64)
|
||||
|
||||
; Pass all arguments in registers and check that we don't adjust stack
|
||||
; for the call.
|
||||
define hhvmcc void @test_helper_short(i64 %a, i64 %b, i64 %c) nounwind {
|
||||
entry:
|
||||
; CHECK-LABEL: test_helper_short:
|
||||
; CHECK-NOT: push
|
||||
; CHECK-NOT: sub
|
||||
; CHECK-DAG: movl $1, %edi
|
||||
; CHECK-DAG: movl $2, %esi
|
||||
; CHECK-DAG: movl $3, %edx
|
||||
; CHECK-DAG: movl $4, %ecx
|
||||
; CHECK-DAG: movl $5, %r8
|
||||
; CHECK-DAG: movl $6, %r9
|
||||
; CHECK: callq helper_short
|
||||
call hhvm_ccc void @helper_short(i64 %c, i64 1, i64 2, i64 3, i64 4,
|
||||
i64 5, i64 6)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare hhvm_ccc void @helper(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64)
|
||||
|
||||
define hhvmcc void @test_helper(i64 %a, i64 %b, i64 %c) nounwind {
|
||||
entry:
|
||||
; CHECK-LABEL: test_helper:
|
||||
; CHECK-DAG: movl $1, %edi
|
||||
; CHECK-DAG: movl $2, %esi
|
||||
; CHECK-DAG: movl $3, %edx
|
||||
; CHECK-DAG: movl $4, %ecx
|
||||
; CHECK-DAG: movl $5, %r8
|
||||
; CHECK-DAG: movl $6, %r9
|
||||
; CHECK: callq helper
|
||||
call hhvm_ccc void @helper(i64 %c, i64 1, i64 2, i64 3, i64 4, i64 5, i64 6,
|
||||
i64 7, i64 8, i64 9)
|
||||
ret void
|
||||
}
|
||||
|
||||
; When we enter function with HHVM calling convention, the stack is aligned
|
||||
; at 16 bytes. This means we align objects on the stack differently and
|
||||
; adjust the stack differently for calls.
|
||||
declare hhvm_ccc void @stack_helper(i64, i64, i64)
|
||||
declare hhvm_ccc void @stack_helper2(<2 x double>, i64)
|
||||
|
||||
define hhvmcc void @test_stack_helper(i64 %a, i64 %b, i64 %c) nounwind {
|
||||
entry:
|
||||
; CHECK-LABEL: test_stack_helper:
|
||||
; CHECK-NOT: push
|
||||
; CHECK: subq $32, %rsp
|
||||
; CHECK: movaps 16(%rsp), %xmm0
|
||||
; CHECK: callq stack_helper2
|
||||
%t1 = alloca <2 x double>, align 16
|
||||
%t2 = alloca i64, align 8
|
||||
%t3 = alloca i64, align 8
|
||||
%load3 = load i64, i64 *%t3
|
||||
call hhvm_ccc void @stack_helper(i64 %c, i64 %load3, i64 42)
|
||||
%load = load <2 x double>, <2 x double> *%t1
|
||||
%load2 = load i64, i64 *%t2
|
||||
call hhvm_ccc void @stack_helper2(<2 x double> %load, i64 %load2)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Check that we are not adjusting the stack before calling the helper.
|
||||
define hhvmcc void @test_stack_helper2(i64 %a, i64 %b, i64 %c) nounwind {
|
||||
entry:
|
||||
; CHECK-LABEL: test_stack_helper2:
|
||||
; CHECK-NOT: push
|
||||
; CHECK-NOT: subq
|
||||
call hhvm_ccc void @stack_helper(i64 %c, i64 7, i64 42)
|
||||
ret void
|
||||
}
|
||||
|
@ -59,4 +59,11 @@ define void @ghc_caller() {
|
||||
ret void
|
||||
}
|
||||
|
||||
declare hhvm_ccc void @hhvm_c_callee()
|
||||
|
||||
define hhvmcc void @hhvm_caller() {
|
||||
call hhvm_ccc void @hhvm_c_callee()
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @__gxx_personality_v0(...)
|
||||
|
@ -183,6 +183,11 @@ TEST(MathExtras, RoundUpToAlignment) {
|
||||
EXPECT_EQ(8u, RoundUpToAlignment(5, 8));
|
||||
EXPECT_EQ(24u, RoundUpToAlignment(17, 8));
|
||||
EXPECT_EQ(0u, RoundUpToAlignment(~0LL, 8));
|
||||
|
||||
EXPECT_EQ(7u, RoundUpToAlignment(5, 8, 7));
|
||||
EXPECT_EQ(17u, RoundUpToAlignment(17, 8, 1));
|
||||
EXPECT_EQ(3u, RoundUpToAlignment(~0LL, 8, 3));
|
||||
EXPECT_EQ(552u, RoundUpToAlignment(321, 255, 42));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,10 +42,11 @@ syn keyword llvmKeyword arm_aapcscc arm_apcscc asm atomic available_externally
|
||||
syn keyword llvmKeyword blockaddress byval c catch cc ccc cleanup coldcc common
|
||||
syn keyword llvmKeyword constant datalayout declare default define deplibs
|
||||
syn keyword llvmKeyword distinct dllexport dllimport except extern_weak external
|
||||
syn keyword llvmKeyword externally_initialized fastcc filter gc global hidden
|
||||
syn keyword llvmKeyword initialexec inlinehint inreg intel_ocl_bicc inteldialect
|
||||
syn keyword llvmKeyword internal linkonce linkonce_odr localdynamic localexec
|
||||
syn keyword llvmKeyword minsize module monotonic msp430_intrcc naked nest
|
||||
syn keyword llvmKeyword externally_initialized fastcc filter gc global hhvmcc
|
||||
syn keyword llvmKeyword hhvm_ccc hidden initialexec inlinehint inreg
|
||||
syn keyword llvmKeyword intel_ocl_bicc inteldialect internal linkonce
|
||||
syn keyword llvmKeyword linkonce_odr localdynamic localexec minsize module
|
||||
syn keyword llvmKeyword monotonic msp430_intrcc musttail naked nest
|
||||
syn keyword llvmKeyword noalias nocapture noimplicitfloat noinline nonlazybind
|
||||
syn keyword llvmKeyword noredzone noreturn nounwind optnone optsize personality
|
||||
syn keyword llvmKeyword private protected ptx_device ptx_kernel readnone
|
||||
|
Loading…
Reference in New Issue
Block a user