Index: lib/Target/X86/X86Subtarget.cpp =================================================================== --- lib/Target/X86/X86Subtarget.cpp +++ lib/Target/X86/X86Subtarget.cpp @@ -140,11 +140,19 @@ assert(!isTargetCOFF()); + const Function *F = dyn_cast_or_null(GV); + + if (is64Bit() && getTargetTriple().isOSLinux() && F && + (CallingConv::X86_RegCall == F->getCallingConv())) + // Lazy binding is disabled in regcall calling convention on 64 bit linux + // because regcall uses XMM8-XMM15 for parameter passing and according to + // the ABI, PLT might clobber those registers. + return X86II::MO_GOTPCREL; + if (isTargetELF()) return X86II::MO_PLT; if (is64Bit()) { - auto *F = dyn_cast_or_null(GV); if (F && F->hasFnAttribute(Attribute::NonLazyBind)) // If the function is marked as non-lazy, generate an indirect call // which loads from the GOT directly. This avoids runtime overhead Index: test/CodeGen/X86/regcall-no-plt.ll =================================================================== --- test/CodeGen/X86/regcall-no-plt.ll +++ test/CodeGen/X86/regcall-no-plt.ll @@ -0,0 +1,43 @@ +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -relocation-model=pic < %s | FileCheck %s + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; According to x86-64 psABI, xmm0-xmm7 can be used to pass function parameters. +;; However regcall calling convention uses also xmm8-xmm15 to pass function +;; parameters which violates x86-64 psABI. +;; Detail info about it can be found at: +;; https://sourceware.org/bugzilla/show_bug.cgi?id=21265 +;; +;; We encounter the violation symptom when using PIC with lazy binding +;; optimization. +;; In that case the PLT mechanism as described in x86_64 psABI will +;; not preserve xmm8-xmm15 registers and will lead to miscompilation. +;; +;; The agreed solution is to disable PLT for regcall calling convention +;; in linux 64 bit. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +declare void @lazy() +declare x86_regcallcc void @regcall_not_lazy() + +; CHECK-LABEL: foo: +; CHECK: callq lazy@PLT +; CHECK: callq *regcall_not_lazy@GOTPCREL(%rip) +define void @foo() nounwind { + call void @lazy() + call void @regcall_not_lazy() + ret void +} + +; CHECK-LABEL: tail_call_regcall: +; CHECK: jmpq *regcall_not_lazy@GOTPCREL(%rip) +define void @tail_call_regcall() nounwind { + tail call void @regcall_not_lazy() + ret void +} + +; CHECK-LABEL: tail_call_regular: +; CHECK: jmp lazy +define void @tail_call_regular() nounwind { + tail call void @lazy() + ret void +}