From 3b3752c898d041fcab9455581136ebc6de921610 Mon Sep 17 00:00:00 2001 From: Maksim Panchenko Date: Tue, 29 Sep 2015 22:09:16 +0000 Subject: [PATCH] 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 --- include/llvm/IR/CallingConv.h | 11 +- include/llvm/Support/MathExtras.h | 16 +- include/llvm/Target/TargetFrameLowering.h | 5 + lib/AsmParser/LLLexer.cpp | 2 + lib/AsmParser/LLParser.cpp | 4 + lib/AsmParser/LLToken.h | 1 + lib/CodeGen/PrologEpilogInserter.cpp | 33 +-- lib/CodeGen/TargetFrameLoweringImpl.cpp | 11 + lib/IR/AsmWriter.cpp | 2 + lib/Target/X86/X86CallingConv.td | 35 ++++ lib/Target/X86/X86ISelLowering.cpp | 12 +- lib/Target/X86/X86RegisterInfo.cpp | 4 + test/CodeGen/X86/hhvm-cc.ll | 241 ++++++++++++++++++++++ test/Feature/callingconventions.ll | 7 + unittests/Support/MathExtrasTest.cpp | 5 + utils/vim/syntax/llvm.vim | 9 +- 16 files changed, 369 insertions(+), 29 deletions(-) create mode 100644 test/CodeGen/X86/hhvm-cc.ll diff --git a/include/llvm/IR/CallingConv.h b/include/llvm/IR/CallingConv.h index f7a45afc9ca..5c82de62dfd 100644 --- a/include/llvm/IR/CallingConv.h +++ b/include/llvm/IR/CallingConv.h @@ -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 diff --git a/include/llvm/Support/MathExtras.h b/include/llvm/Support/MathExtras.h index df18b6fcfca..71b22b0f2fc 100644 --- a/include/llvm/Support/MathExtras.h +++ b/include/llvm/Support/MathExtras.h @@ -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 diff --git a/include/llvm/Target/TargetFrameLowering.h b/include/llvm/Target/TargetFrameLowering.h index 61e3afd77b3..3485ed6df8b 100644 --- a/include/llvm/Target/TargetFrameLowering.h +++ b/include/llvm/Target/TargetFrameLowering.h @@ -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. /// diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp index b166c17b124..2eb5f0bf45d 100644 --- a/lib/AsmParser/LLLexer.cpp +++ b/lib/AsmParser/LLLexer.cpp @@ -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); diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp index b99d8579ce3..02b4b75b040 100644 --- a/lib/AsmParser/LLParser.cpp +++ b/lib/AsmParser/LLParser.cpp @@ -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); diff --git a/lib/AsmParser/LLToken.h b/lib/AsmParser/LLToken.h index b83ca2c652f..06b5f9b0800 100644 --- a/lib/AsmParser/LLToken.h +++ b/lib/AsmParser/LLToken.h @@ -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, diff --git a/lib/CodeGen/PrologEpilogInserter.cpp b/lib/CodeGen/PrologEpilogInserter.cpp index c7398ad00a0..63825b5dde9 100644 --- a/lib/CodeGen/PrologEpilogInserter.cpp +++ b/lib/CodeGen/PrologEpilogInserter.cpp @@ -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 &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::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::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... diff --git a/lib/CodeGen/TargetFrameLoweringImpl.cpp b/lib/CodeGen/TargetFrameLoweringImpl.cpp index 847027e2819..679ade185e1 100644 --- a/lib/CodeGen/TargetFrameLoweringImpl.cpp +++ b/lib/CodeGen/TargetFrameLoweringImpl.cpp @@ -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; +} diff --git a/lib/IR/AsmWriter.cpp b/lib/IR/AsmWriter.cpp index 7731d332a65..b4d7a660688 100644 --- a/lib/IR/AsmWriter.cpp +++ b/lib/IR/AsmWriter.cpp @@ -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; } } diff --git a/lib/Target/X86/X86CallingConv.td b/lib/Target/X86/X86CallingConv.td index 8f88888f5ce..aaabf7a59f5 100644 --- a/lib/Target/X86/X86CallingConv.td +++ b/lib/Target/X86/X86CallingConv.td @@ -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>, + + // 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>, CCIfCC<"CallingConv::X86_64_SysV", CCDelegateTo>, + // Handle HHVM calls. + CCIfCC<"CallingConv::HHVM", CCDelegateTo>, + // Mingw64 and native Win64 use Win64 CC CCIfSubtarget<"isTargetWin64()", CCDelegateTo>, @@ -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 +]>; + // 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>, CCIfCC<"CallingConv::X86_64_SysV", CCDelegateTo>, CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo>, + CCIfCC<"CallingConv::HHVM", CCDelegateTo>, + CCIfCC<"CallingConv::HHVM_C", CCDelegateTo>, // Mingw64 and native Win64 use Win64 CC CCIfSubtarget<"isTargetWin64()", CCDelegateTo>, @@ -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)>; diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index e4e32c688ea..21a72fd1fab 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -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; } } diff --git a/lib/Target/X86/X86RegisterInfo.cpp b/lib/Target/X86/X86RegisterInfo.cpp index 7b04e81c483..8531538001d 100644 --- a/lib/Target/X86/X86RegisterInfo.cpp +++ b/lib/Target/X86/X86RegisterInfo.cpp @@ -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; diff --git a/test/CodeGen/X86/hhvm-cc.ll b/test/CodeGen/X86/hhvm-cc.ll new file mode 100644 index 00000000000..3b729ed72f1 --- /dev/null +++ b/test/CodeGen/X86/hhvm-cc.ll @@ -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 +} + diff --git a/test/Feature/callingconventions.ll b/test/Feature/callingconventions.ll index 9aafb36c757..ac4c5090a51 100644 --- a/test/Feature/callingconventions.ll +++ b/test/Feature/callingconventions.ll @@ -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(...) diff --git a/unittests/Support/MathExtrasTest.cpp b/unittests/Support/MathExtrasTest.cpp index 5c95b500dd4..8bd47e34c7e 100644 --- a/unittests/Support/MathExtrasTest.cpp +++ b/unittests/Support/MathExtrasTest.cpp @@ -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)); } } diff --git a/utils/vim/syntax/llvm.vim b/utils/vim/syntax/llvm.vim index 8eaa0d6e3fc..422b0971ac8 100644 --- a/utils/vim/syntax/llvm.vim +++ b/utils/vim/syntax/llvm.vim @@ -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 -- 2.34.1