From 9f275a29d5b0236291a1396c1ad0aa809dd77c86 Mon Sep 17 00:00:00 2001 From: zhijian Date: Wed, 13 Aug 2025 18:25:55 +0000 Subject: [PATCH 1/4] implement using milicode for strlen instead of libcall --- llvm/include/llvm/CodeGen/SelectionDAG.h | 10 +++-- .../llvm/CodeGen/SelectionDAGTargetInfo.h | 2 +- llvm/include/llvm/IR/RuntimeLibcalls.td | 3 ++ .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 37 +++++++++++++++++++ .../SelectionDAG/SelectionDAGBuilder.cpp | 5 +-- .../Target/PowerPC/PPCSelectionDAGInfo.cpp | 8 ++++ llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.h | 3 ++ .../SystemZ/SystemZSelectionDAGInfo.cpp | 2 +- .../Target/SystemZ/SystemZSelectionDAGInfo.h | 3 +- llvm/test/CodeGen/PowerPC/milicode32.ll | 2 +- llvm/test/CodeGen/PowerPC/milicode64.ll | 3 +- 11 files changed, 66 insertions(+), 12 deletions(-) diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h index dc00db9daa3b6..831e274b22ec4 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -1256,9 +1256,13 @@ class SelectionDAG { /// stack arguments from being clobbered. LLVM_ABI SDValue getStackArgumentTokenFactor(SDValue Chain); - std::pair getMemcmp(SDValue Chain, const SDLoc &dl, - SDValue Dst, SDValue Src, SDValue Size, - const CallInst *CI); + LLVM_ABI std::pair getMemcmp(SDValue Chain, const SDLoc &dl, + SDValue Dst, SDValue Src, + SDValue Size, + const CallInst *CI); + LLVM_ABI std::pair + getStrlen(SDValue Chain, const SDLoc &dl, SDValue Src, const CallInst *CI); + /* \p CI if not null is the memset call being lowered. * \p OverrideTailCall is an optional parameter that can be used to override * the tail call optimization decision. */ diff --git a/llvm/include/llvm/CodeGen/SelectionDAGTargetInfo.h b/llvm/include/llvm/CodeGen/SelectionDAGTargetInfo.h index fd00f813bc9c3..fbfb240cae449 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAGTargetInfo.h +++ b/llvm/include/llvm/CodeGen/SelectionDAGTargetInfo.h @@ -162,7 +162,7 @@ class SelectionDAGTargetInfo { virtual std::pair EmitTargetCodeForStrlen(SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, - SDValue Src, MachinePointerInfo SrcPtrInfo) const { + SDValue Src, const CallInst *CI) const { return std::make_pair(SDValue(), SDValue()); } diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td b/llvm/include/llvm/IR/RuntimeLibcalls.td index 9072a0aa1531f..3fe98b367cb01 100644 --- a/llvm/include/llvm/IR/RuntimeLibcalls.td +++ b/llvm/include/llvm/IR/RuntimeLibcalls.td @@ -282,6 +282,7 @@ def MEMMOVE : RuntimeLibcall; def MEMSET : RuntimeLibcall; def CALLOC : RuntimeLibcall; def BZERO : RuntimeLibcall; +def STRLEN : RuntimeLibcall; // Element-wise unordered-atomic memory of different sizes foreach MemSize = [1, 2, 4, 8, 16] in { @@ -2115,6 +2116,7 @@ defset list PPC64AIXCallList = { def ___memmove64 : RuntimeLibcallImpl; def ___memset64 : RuntimeLibcallImpl; def ___bzero64 : RuntimeLibcallImpl; + def ___strlen64 : RuntimeLibcallImpl; } defset list PPC32AIXCallList = { @@ -2122,6 +2124,7 @@ defset list PPC32AIXCallList = { def ___memmove : RuntimeLibcallImpl; def ___memset : RuntimeLibcallImpl; def ___bzero : RuntimeLibcallImpl; + def ___strlen : RuntimeLibcallImpl; } defvar PPCOverrides = !foreach(entry, PPCRuntimeLibcalls, entry.Provides); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 4b7fc45908119..61674f9fa55f8 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -9050,6 +9050,43 @@ SelectionDAG::getMemcmp(SDValue Chain, const SDLoc &dl, SDValue Mem0, return TLI->LowerCallTo(CLI); } +std::pair SelectionDAG::getStrlen(SDValue Chain, + const SDLoc &dl, + SDValue Src, + const CallInst *CI) { + const char *LibCallName = TLI->getLibcallName(RTLIB::STRLEN); + if (!LibCallName) + return {}; + + // Emit a library call. + auto GetEntry = [](Type *Ty, SDValue &SDV) { + TargetLowering::ArgListEntry E; + E.Ty = Ty; + E.Node = SDV; + return E; + }; + + PointerType *PT = PointerType::getUnqual(*getContext()); + TargetLowering::ArgListTy Args = {GetEntry(PT, Src)}; + + TargetLowering::CallLoweringInfo CLI(*this); + bool IsTailCall = false; + bool ReturnsFirstArg = CI && funcReturnsFirstArgOfCall(*CI); + IsTailCall = CI && CI->isTailCall() && + isInTailCallPosition(*CI, getTarget(), ReturnsFirstArg); + + CLI.setDebugLoc(dl) + .setChain(Chain) + .setLibCallee( + TLI->getLibcallCallingConv(RTLIB::STRLEN), + Type::getInt32Ty(*getContext()), + getExternalSymbol(LibCallName, TLI->getPointerTy(getDataLayout())), + std::move(Args)) + .setTailCall(IsTailCall); + + return TLI->LowerCallTo(CLI); +} + SDValue SelectionDAG::getMemcpy( SDValue Chain, const SDLoc &dl, SDValue Dst, SDValue Src, SDValue Size, Align Alignment, bool isVol, bool AlwaysInline, const CallInst *CI, diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 366a230eef952..9a75f417e2af8 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -9287,9 +9287,8 @@ bool SelectionDAGBuilder::visitStrLenCall(const CallInst &I) { const Value *Arg0 = I.getArgOperand(0); const SelectionDAGTargetInfo &TSI = DAG.getSelectionDAGInfo(); - std::pair Res = - TSI.EmitTargetCodeForStrlen(DAG, getCurSDLoc(), DAG.getRoot(), - getValue(Arg0), MachinePointerInfo(Arg0)); + std::pair Res = TSI.EmitTargetCodeForStrlen( + DAG, getCurSDLoc(), DAG.getRoot(), getValue(Arg0), &I); if (Res.first.getNode()) { processIntegerCallValue(I, Res.first, false); PendingLoads.push_back(Res.second); diff --git a/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp b/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp index 4039fedd0cb5c..e15b5b0bea2fc 100644 --- a/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp +++ b/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp @@ -28,3 +28,11 @@ std::pair PPCSelectionDAGInfo::EmitTargetCodeForMemcmp( SDValue Op3, const CallInst *CI) const { return DAG.getMemcmp(Chain, dl, Op1, Op2, Op3, CI); } + +std::pair +PPCSelectionDAGInfo::EmitTargetCodeForStrlen(SelectionDAG &DAG, const SDLoc &DL, + SDValue Chain, SDValue Src, + const CallInst *CI) const { + EVT PtrVT = Src.getValueType(); + return DAG.getStrlen(Chain, DL, Src, CI); +} diff --git a/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.h b/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.h index 1537851a1b610..f962a7a5321aa 100644 --- a/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.h +++ b/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.h @@ -25,6 +25,9 @@ class PPCSelectionDAGInfo : public SelectionDAGTargetInfo { EmitTargetCodeForMemcmp(SelectionDAG &DAG, const SDLoc &dl, SDValue Chain, SDValue Op1, SDValue Op2, SDValue Op3, const CallInst *CI) const override; + std::pair + EmitTargetCodeForStrlen(SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, + SDValue Src, const CallInst *CI) const override; }; } // namespace llvm diff --git a/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp b/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp index afe838ac973e6..eb00d484af693 100644 --- a/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp +++ b/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp @@ -263,7 +263,7 @@ static std::pair getBoundedStrlen(SelectionDAG &DAG, std::pair SystemZSelectionDAGInfo::EmitTargetCodeForStrlen( SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Src, - MachinePointerInfo SrcPtrInfo) const { + const CallInst *CI) const { EVT PtrVT = Src.getValueType(); return getBoundedStrlen(DAG, DL, Chain, Src, DAG.getConstant(0, DL, PtrVT)); } diff --git a/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.h b/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.h index 5a1e0cd108e77..200566f9646c1 100644 --- a/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.h +++ b/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.h @@ -61,8 +61,7 @@ class SystemZSelectionDAGInfo : public SelectionDAGTargetInfo { std::pair EmitTargetCodeForStrlen(SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, - SDValue Src, - MachinePointerInfo SrcPtrInfo) const override; + SDValue Src, const CallInst *CI) const override; std::pair EmitTargetCodeForStrnlen(SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, diff --git a/llvm/test/CodeGen/PowerPC/milicode32.ll b/llvm/test/CodeGen/PowerPC/milicode32.ll index a2af6d413b4bf..78d036202fe4e 100644 --- a/llvm/test/CodeGen/PowerPC/milicode32.ll +++ b/llvm/test/CodeGen/PowerPC/milicode32.ll @@ -42,7 +42,7 @@ define i32 @strlen_test(ptr noundef %str) nounwind { ; CHECK-AIX-32-P9-NEXT: stwu r1, -64(r1) ; CHECK-AIX-32-P9-NEXT: stw r0, 72(r1) ; CHECK-AIX-32-P9-NEXT: stw r3, 60(r1) -; CHECK-AIX-32-P9-NEXT: bl .strlen[PR] +; CHECK-AIX-32-P9-NEXT: bl .___strlen[PR] ; CHECK-AIX-32-P9-NEXT: nop ; CHECK-AIX-32-P9-NEXT: addi r1, r1, 64 ; CHECK-AIX-32-P9-NEXT: lwz r0, 8(r1) diff --git a/llvm/test/CodeGen/PowerPC/milicode64.ll b/llvm/test/CodeGen/PowerPC/milicode64.ll index 0f0585d9028a9..31d8704b7974c 100644 --- a/llvm/test/CodeGen/PowerPC/milicode64.ll +++ b/llvm/test/CodeGen/PowerPC/milicode64.ll @@ -85,8 +85,9 @@ define i64 @strlen_test(ptr noundef %str) nounwind { ; CHECK-AIX-64-P9-NEXT: stdu r1, -128(r1) ; CHECK-AIX-64-P9-NEXT: std r0, 144(r1) ; CHECK-AIX-64-P9-NEXT: std r3, 120(r1) -; CHECK-AIX-64-P9-NEXT: bl .strlen[PR] +; CHECK-AIX-64-P9-NEXT: bl .___strlen64[PR] ; CHECK-AIX-64-P9-NEXT: nop +; CHECK-AIX-64-P9-NEXT: clrldi r3, r3, 32 ; CHECK-AIX-64-P9-NEXT: addi r1, r1, 128 ; CHECK-AIX-64-P9-NEXT: ld r0, 16(r1) ; CHECK-AIX-64-P9-NEXT: mtlr r0 From 8ed02ded0bf591edad32488fdead401acd3b1e0f Mon Sep 17 00:00:00 2001 From: zhijian Date: Thu, 14 Aug 2025 14:06:05 +0000 Subject: [PATCH 2/4] fix an error --- llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 3 +-- llvm/test/CodeGen/PowerPC/milicode64.ll | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 61674f9fa55f8..a1f3a9b201a30 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -9078,8 +9078,7 @@ std::pair SelectionDAG::getStrlen(SDValue Chain, CLI.setDebugLoc(dl) .setChain(Chain) .setLibCallee( - TLI->getLibcallCallingConv(RTLIB::STRLEN), - Type::getInt32Ty(*getContext()), + TLI->getLibcallCallingConv(RTLIB::STRLEN), CI->getType(), getExternalSymbol(LibCallName, TLI->getPointerTy(getDataLayout())), std::move(Args)) .setTailCall(IsTailCall); diff --git a/llvm/test/CodeGen/PowerPC/milicode64.ll b/llvm/test/CodeGen/PowerPC/milicode64.ll index 31d8704b7974c..8b87529d9a6d8 100644 --- a/llvm/test/CodeGen/PowerPC/milicode64.ll +++ b/llvm/test/CodeGen/PowerPC/milicode64.ll @@ -87,7 +87,6 @@ define i64 @strlen_test(ptr noundef %str) nounwind { ; CHECK-AIX-64-P9-NEXT: std r3, 120(r1) ; CHECK-AIX-64-P9-NEXT: bl .___strlen64[PR] ; CHECK-AIX-64-P9-NEXT: nop -; CHECK-AIX-64-P9-NEXT: clrldi r3, r3, 32 ; CHECK-AIX-64-P9-NEXT: addi r1, r1, 128 ; CHECK-AIX-64-P9-NEXT: ld r0, 16(r1) ; CHECK-AIX-64-P9-NEXT: mtlr r0 From 56fa506b61a826267b59ae281dca3115f0c67496 Mon Sep 17 00:00:00 2001 From: zhijian Date: Wed, 20 Aug 2025 17:47:46 +0000 Subject: [PATCH 3/4] address comment --- .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 26 +++++++++++-------- .../Target/PowerPC/PPCSelectionDAGInfo.cpp | 1 - 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index a1f3a9b201a30..a2946736682a6 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -9070,18 +9070,22 @@ std::pair SelectionDAG::getStrlen(SDValue Chain, TargetLowering::ArgListTy Args = {GetEntry(PT, Src)}; TargetLowering::CallLoweringInfo CLI(*this); - bool IsTailCall = false; - bool ReturnsFirstArg = CI && funcReturnsFirstArgOfCall(*CI); - IsTailCall = CI && CI->isTailCall() && - isInTailCallPosition(*CI, getTarget(), ReturnsFirstArg); - CLI.setDebugLoc(dl) - .setChain(Chain) - .setLibCallee( - TLI->getLibcallCallingConv(RTLIB::STRLEN), CI->getType(), - getExternalSymbol(LibCallName, TLI->getPointerTy(getDataLayout())), - std::move(Args)) - .setTailCall(IsTailCall); + // TODO: Intentionally not marking this libcall as a tail call. + // + // Why: + // - The only current in-tree user of SelectionDAG::getStrlen is the AIX path, + // where generic tail-calling to libcalls is not safe due to ABI + // constraints around r2 (TOC). We don¡¯t have a reliable way to validate a + // tail call here. + // + // If another target starts using this and does support tail calls to + // libcalls, re-enable `.setTailCall(...)` under a target guard and add a + // test. + CLI.setDebugLoc(dl).setChain(Chain).setLibCallee( + TLI->getLibcallCallingConv(RTLIB::STRLEN), CI->getType(), + getExternalSymbol(LibCallName, TLI->getProgramPointerTy(getDataLayout())), + std::move(Args)); return TLI->LowerCallTo(CLI); } diff --git a/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp b/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp index e15b5b0bea2fc..93a4693c50168 100644 --- a/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp +++ b/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp @@ -33,6 +33,5 @@ std::pair PPCSelectionDAGInfo::EmitTargetCodeForStrlen(SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Src, const CallInst *CI) const { - EVT PtrVT = Src.getValueType(); return DAG.getStrlen(Chain, DL, Src, CI); } From 0c7f77469fa97558f8bf2ad9f2defabecea154ca Mon Sep 17 00:00:00 2001 From: zhijian Date: Fri, 22 Aug 2025 18:14:37 +0000 Subject: [PATCH 4/4] address comment --- llvm/include/llvm/CodeGen/SelectionDAG.h | 7 +++---- llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 13 ++----------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h index 831e274b22ec4..9d43b2435b672 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -1256,10 +1256,9 @@ class SelectionDAG { /// stack arguments from being clobbered. LLVM_ABI SDValue getStackArgumentTokenFactor(SDValue Chain); - LLVM_ABI std::pair getMemcmp(SDValue Chain, const SDLoc &dl, - SDValue Dst, SDValue Src, - SDValue Size, - const CallInst *CI); + std::pair getMemcmp(SDValue Chain, const SDLoc &dl, + SDValue Dst, SDValue Src, SDValue Size, + const CallInst *CI); LLVM_ABI std::pair getStrlen(SDValue Chain, const SDLoc &dl, SDValue Src, const CallInst *CI); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index a2946736682a6..2b259ef3036d2 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -9071,17 +9071,8 @@ std::pair SelectionDAG::getStrlen(SDValue Chain, TargetLowering::CallLoweringInfo CLI(*this); - // TODO: Intentionally not marking this libcall as a tail call. - // - // Why: - // - The only current in-tree user of SelectionDAG::getStrlen is the AIX path, - // where generic tail-calling to libcalls is not safe due to ABI - // constraints around r2 (TOC). We don¡¯t have a reliable way to validate a - // tail call here. - // - // If another target starts using this and does support tail calls to - // libcalls, re-enable `.setTailCall(...)` under a target guard and add a - // test. + // TODO: propagate tail call flag for targets where that is safe. Note + // that it is not safe on AIX which is the only current target. CLI.setDebugLoc(dl).setChain(Chain).setLibCallee( TLI->getLibcallCallingConv(RTLIB::STRLEN), CI->getType(), getExternalSymbol(LibCallName, TLI->getProgramPointerTy(getDataLayout())),