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