Index: lib/Target/Mips/MipsISelLowering.cpp =================================================================== --- lib/Target/Mips/MipsISelLowering.cpp +++ lib/Target/Mips/MipsISelLowering.cpp @@ -3152,15 +3152,30 @@ // The long-calls feature is ignored in case of PIC. // While we do not support -mshared / -mno-shared properly, // ignore long-calls in case of -mabicalls too. - if (Subtarget.useLongCalls() && !Subtarget.isABICalls() && !IsPIC) { - // Get the address of the callee into a register to prevent - // using of the `jal` instruction for the direct call. - if (auto *N = dyn_cast(Callee)) - Callee = Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) - : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); - else if (auto *N = dyn_cast(Callee)) - Callee = Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) - : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); + if (!Subtarget.isABICalls() && !IsPIC) { + // If the function should be called using "long call", + // get its address into a register to prevent using + // of the `jal` instruction for the direct call. + if (auto *N = dyn_cast(Callee)) { + if (Subtarget.useLongCalls()) + Callee = Subtarget.hasSym32() + ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) + : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); + } else if (auto *N = dyn_cast(Callee)) { + bool UseLongCalls = Subtarget.useLongCalls(); + // If the function has long-call/far/near attribute + // it overrides command line switch pased to the backend. + if (auto *F = dyn_cast(N->getGlobal())) { + if (F->hasFnAttribute("long-call")) + UseLongCalls = true; + else if (F->hasFnAttribute("short-call")) + UseLongCalls = false; + } + if (UseLongCalls) + Callee = Subtarget.hasSym32() + ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) + : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); + } } if (GlobalAddressSDNode *G = dyn_cast(Callee)) { Index: test/CodeGen/Mips/long-call-attr.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/long-call-attr.ll @@ -0,0 +1,42 @@ +; RUN: llc -march=mips -target-abi o32 --mattr=+long-calls,+noabicalls < %s \ +; RUN: | FileCheck -check-prefix=O32 %s +; RUN: llc -march=mips -target-abi o32 --mattr=-long-calls,+noabicalls < %s \ +; RUN: | FileCheck -check-prefix=O32 %s +; RUN: llc -march=mips64 -target-abi n64 --mattr=+long-calls,+noabicalls < %s \ +; RUN: | FileCheck -check-prefix=N64 %s +; RUN: llc -march=mips64 -target-abi n64 --mattr=-long-calls,+noabicalls < %s \ +; RUN: | FileCheck -check-prefix=N64 %s + +declare void @far() #0 + +define void @near() #1 { + ret void +} + +define void @foo() { + call void @far() + +; O32-LABEL: foo: +; O32: lui $1, %hi(far) +; O32-NEXT: addiu $25, $1, %lo(far) +; O32-NEXT: jalr $25 + +; N64-LABEL: foo: +; N64: lui $1, %highest(far) +; N64-NEXT: daddiu $1, $1, %higher(far) +; N64-NEXT: dsll $1, $1, 16 +; N64-NEXT: daddiu $1, $1, %hi(far) +; N64-NEXT: dsll $1, $1, 16 +; N64-NEXT: daddiu $25, $1, %lo(far) +; N64-NEXT: jalr $25 + + call void @near() + +; O32: jal near +; N64: jal near + + ret void +} + +attributes #0 = { "long-call" } +attributes #1 = { "short-call" }