From 4fc7355a21e1fa838406e15459aaf54a58fcf909 Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Fri, 16 Aug 2013 11:29:37 +0000 Subject: [PATCH] [SystemZ] Use MVST to implement strcpy and stpcpy git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@188546 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Target/TargetLibraryInfo.h | 3 +- include/llvm/Target/TargetSelectionDAGInfo.h | 15 ++++++ .../SelectionDAG/SelectionDAGBuilder.cpp | 37 ++++++++++++++ .../SelectionDAG/SelectionDAGBuilder.h | 1 + lib/Target/SystemZ/SystemZISelLowering.cpp | 3 ++ lib/Target/SystemZ/SystemZISelLowering.h | 3 ++ lib/Target/SystemZ/SystemZInstrInfo.td | 4 ++ lib/Target/SystemZ/SystemZOperators.td | 2 + .../SystemZ/SystemZSelectionDAGInfo.cpp | 11 ++++ lib/Target/SystemZ/SystemZSelectionDAGInfo.h | 7 +++ test/CodeGen/SystemZ/strcpy-01.ll | 50 +++++++++++++++++++ test/MC/Disassembler/SystemZ/insns.txt | 12 +++++ test/MC/SystemZ/insn-good.s | 10 ++++ 13 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 test/CodeGen/SystemZ/strcpy-01.ll diff --git a/include/llvm/Target/TargetLibraryInfo.h b/include/llvm/Target/TargetLibraryInfo.h index a033d07387a..7a06415f4b6 100644 --- a/include/llvm/Target/TargetLibraryInfo.h +++ b/include/llvm/Target/TargetLibraryInfo.h @@ -699,7 +699,8 @@ public: case LibFunc::trunc: case LibFunc::truncf: case LibFunc::truncl: case LibFunc::log2: case LibFunc::log2f: case LibFunc::log2l: case LibFunc::exp2: case LibFunc::exp2f: case LibFunc::exp2l: - case LibFunc::memcmp: case LibFunc::strcmp: + case LibFunc::memcmp: case LibFunc::strcmp: case LibFunc::strcpy: + case LibFunc::stpcpy: return true; } return false; diff --git a/include/llvm/Target/TargetSelectionDAGInfo.h b/include/llvm/Target/TargetSelectionDAGInfo.h index 2d0989c8ffe..5e65c304a1d 100644 --- a/include/llvm/Target/TargetSelectionDAGInfo.h +++ b/include/llvm/Target/TargetSelectionDAGInfo.h @@ -109,6 +109,21 @@ public: return std::make_pair(SDValue(), SDValue()); } + /// EmitTargetCodeForStrcpy - Emit target-specific code that performs a + /// strcpy or stpcpy, in cases where that is faster than a libcall. + /// The first returned SDValue is the result of the copy (the start + /// of the destination string for strcpy, a pointer to the null terminator + /// for stpcpy) and the second is the chain. Both SDValues can be null + /// if a normal libcall should be used. + virtual std::pair + EmitTargetCodeForStrcpy(SelectionDAG &DAG, SDLoc DL, SDValue Chain, + SDValue Dest, SDValue Src, + MachinePointerInfo DestPtrInfo, + MachinePointerInfo SrcPtrInfo, + bool isStpcpy) const { + return std::make_pair(SDValue(), SDValue()); + } + /// EmitTargetCodeForStrcmp - Emit target-specific code that performs a /// strcmp, in cases where that is faster than a libcall. The first /// returned SDValue is the result of the strcmp and the second is diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index b1812f2cda9..9bb78978e11 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5558,6 +5558,35 @@ bool SelectionDAGBuilder::visitMemCmpCall(const CallInst &I) { return false; } +/// visitStrCpyCall -- See if we can lower a strcpy or stpcpy call into an +/// optimized form. If so, return true and lower it, otherwise return false +/// and it will be lowered like a normal call. +bool SelectionDAGBuilder::visitStrCpyCall(const CallInst &I, bool isStpcpy) { + // Verify that the prototype makes sense. char *strcpy(char *, char *) + if (I.getNumArgOperands() != 2) + return false; + + const Value *Arg0 = I.getArgOperand(0), *Arg1 = I.getArgOperand(1); + if (!Arg0->getType()->isPointerTy() || + !Arg1->getType()->isPointerTy() || + !I.getType()->isPointerTy()) + return false; + + const TargetSelectionDAGInfo &TSI = DAG.getSelectionDAGInfo(); + std::pair Res = + TSI.EmitTargetCodeForStrcpy(DAG, getCurSDLoc(), getRoot(), + getValue(Arg0), getValue(Arg1), + MachinePointerInfo(Arg0), + MachinePointerInfo(Arg1), isStpcpy); + if (Res.first.getNode()) { + setValue(&I, Res.first); + DAG.setRoot(Res.second); + return true; + } + + return false; +} + /// visitStrCmpCall - See if we can lower a call to strcmp in an optimized form. /// If so, return true and lower it, otherwise return false and it will be /// lowered like a normal call. @@ -5733,6 +5762,14 @@ void SelectionDAGBuilder::visitCall(const CallInst &I) { if (visitMemCmpCall(I)) return; break; + case LibFunc::strcpy: + if (visitStrCpyCall(I, false)) + return; + break; + case LibFunc::stpcpy: + if (visitStrCpyCall(I, true)) + return; + break; case LibFunc::strcmp: if (visitStrCmpCall(I)) return; diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h index ff15347aae5..7c6e671b73f 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -523,6 +523,7 @@ private: void visitPHI(const PHINode &I); void visitCall(const CallInst &I); bool visitMemCmpCall(const CallInst &I); + bool visitStrCpyCall(const CallInst &I, bool isStpcpy); bool visitStrCmpCall(const CallInst &I); bool visitUnaryFloatCall(const CallInst &I, unsigned Opcode); void visitAtomicLoad(const LoadInst &I); diff --git a/lib/Target/SystemZ/SystemZISelLowering.cpp b/lib/Target/SystemZ/SystemZISelLowering.cpp index 1fe54f1280f..b22cc40155d 100644 --- a/lib/Target/SystemZ/SystemZISelLowering.cpp +++ b/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -1703,6 +1703,7 @@ const char *SystemZTargetLowering::getTargetNodeName(unsigned Opcode) const { OPCODE(MVC); OPCODE(CLC); OPCODE(STRCMP); + OPCODE(STPCPY); OPCODE(IPM); OPCODE(ATOMIC_SWAPW); OPCODE(ATOMIC_LOADW_ADD); @@ -2551,6 +2552,8 @@ EmitInstrWithCustomInserter(MachineInstr *MI, MachineBasicBlock *MBB) const { return emitMemMemWrapper(MI, MBB, SystemZ::CLC); case SystemZ::CLSTLoop: return emitStringWrapper(MI, MBB, SystemZ::CLST); + case SystemZ::MVSTLoop: + return emitStringWrapper(MI, MBB, SystemZ::MVST); default: llvm_unreachable("Unexpected instr type to insert"); } diff --git a/lib/Target/SystemZ/SystemZISelLowering.h b/lib/Target/SystemZ/SystemZISelLowering.h index 4ee87d3cb8e..b27f1672e83 100644 --- a/lib/Target/SystemZ/SystemZISelLowering.h +++ b/lib/Target/SystemZ/SystemZISelLowering.h @@ -84,6 +84,9 @@ namespace SystemZISD { // as for MVC. CLC, + // Use an MVST-based sequence to implement stpcpy(). + STPCPY, + // Use a CLST-based sequence to implement strcmp(). The two input operands // are the addresses of the strings to compare. STRCMP, diff --git a/lib/Target/SystemZ/SystemZInstrInfo.td b/lib/Target/SystemZ/SystemZInstrInfo.td index 31832f70d29..43537aa3cc1 100644 --- a/lib/Target/SystemZ/SystemZInstrInfo.td +++ b/lib/Target/SystemZ/SystemZInstrInfo.td @@ -336,6 +336,10 @@ def MVGHI : StoreSIL<"mvghi", 0xE548, store, imm64sx16>; let mayLoad = 1, mayStore = 1 in defm MVC : MemorySS<"mvc", 0xD2, z_mvc>; +// String moves. +let mayLoad = 1, mayStore = 1, Defs = [CC], Uses = [R0W] in + defm MVST : StringRRE<"mvst", 0xB255, z_stpcpy>; + defm LoadStore8_32 : MVCLoadStore; defm LoadStore16_32 : MVCLoadStore; def z_strcmp : SDNode<"SystemZISD::STRCMP", SDT_ZString, [SDNPHasChain, SDNPOutGlue, SDNPMayLoad]>; +def z_stpcpy : SDNode<"SystemZISD::STPCPY", SDT_ZString, + [SDNPHasChain, SDNPMayStore, SDNPMayLoad]>; def z_ipm : SDNode<"SystemZISD::IPM", SDT_ZI32Intrinsic, [SDNPInGlue]>; diff --git a/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp b/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp index 789594b36c6..0a2080db0b9 100644 --- a/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp +++ b/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp @@ -158,6 +158,17 @@ EmitTargetCodeForMemcmp(SelectionDAG &DAG, SDLoc DL, SDValue Chain, return std::make_pair(SDValue(), SDValue()); } +std::pair SystemZSelectionDAGInfo:: +EmitTargetCodeForStrcpy(SelectionDAG &DAG, SDLoc DL, SDValue Chain, + SDValue Dest, SDValue Src, + MachinePointerInfo DestPtrInfo, + MachinePointerInfo SrcPtrInfo, bool isStpcpy) const { + SDVTList VTs = DAG.getVTList(Dest.getValueType(), MVT::Other); + SDValue EndDest = DAG.getNode(SystemZISD::STPCPY, DL, VTs, Chain, Dest, Src, + DAG.getConstant(0, MVT::i32)); + return std::make_pair(isStpcpy ? EndDest : Dest, EndDest.getValue(1)); +} + std::pair SystemZSelectionDAGInfo:: EmitTargetCodeForStrcmp(SelectionDAG &DAG, SDLoc DL, SDValue Chain, SDValue Src1, SDValue Src2, diff --git a/lib/Target/SystemZ/SystemZSelectionDAGInfo.h b/lib/Target/SystemZ/SystemZSelectionDAGInfo.h index c874a4d1925..123cb63c09c 100644 --- a/lib/Target/SystemZ/SystemZSelectionDAGInfo.h +++ b/lib/Target/SystemZ/SystemZSelectionDAGInfo.h @@ -46,6 +46,13 @@ public: MachinePointerInfo Op1PtrInfo, MachinePointerInfo Op2PtrInfo) const LLVM_OVERRIDE; + virtual std::pair + EmitTargetCodeForStrcpy(SelectionDAG &DAG, SDLoc DL, SDValue Chain, + SDValue Dest, SDValue Src, + MachinePointerInfo DestPtrInfo, + MachinePointerInfo SrcPtrInfo, + bool isStpcpy) const LLVM_OVERRIDE; + virtual std::pair EmitTargetCodeForStrcmp(SelectionDAG &DAG, SDLoc DL, SDValue Chain, SDValue Src1, SDValue Src2, diff --git a/test/CodeGen/SystemZ/strcpy-01.ll b/test/CodeGen/SystemZ/strcpy-01.ll new file mode 100644 index 00000000000..29bab629ecf --- /dev/null +++ b/test/CodeGen/SystemZ/strcpy-01.ll @@ -0,0 +1,50 @@ +; Test strcpy using MVST. +; +; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s + +declare i8 *@strcpy(i8 *%dest, i8 *%src) +declare i8 *@stpcpy(i8 *%dest, i8 *%src) + +; Check strcpy. +define i8 *@f1(i8 *%dest, i8 *%src) { +; CHECK-LABEL: f1: +; CHECK-DAG: lhi %r0, 0 +; CHECK-DAG: lgr [[REG:%r[145]]], %r2 +; CHECK: [[LABEL:\.[^:]*]]: +; CHECK-NEXT: mvst [[REG]], %r3 +; CHECK-NEXT: jo [[LABEL]] +; CHECK-NOT: %r2 +; CHECK: br %r14 + %res = call i8 *@strcpy(i8 *%dest, i8 *%src) + ret i8 *%res +} + +; Check stpcpy. +define i8 *@f2(i8 *%dest, i8 *%src) { +; CHECK-LABEL: f2: +; CHECK: lhi %r0, 0 +; CHECK: [[LABEL:\.[^:]*]]: +; CHECK-NEXT: mvst %r2, %r3 +; CHECK-NEXT: jo [[LABEL]] +; CHECK-NOT: %r2 +; CHECK: br %r14 + %res = call i8 *@stpcpy(i8 *%dest, i8 *%src) + ret i8 *%res +} + +; Check correct operation with other loads and stores. The load must +; come before the loop and the store afterwards. +define i32 @f3(i32 %dummy, i8 *%dest, i8 *%src, i32 *%resptr, i32 *%storeptr) { +; CHECK-LABEL: f3: +; CHECK-DAG: lhi %r0, 0 +; CHECK-DAG: l %r2, 0(%r5) +; CHECK: [[LABEL:\.[^:]*]]: +; CHECK-NEXT: mvst %r3, %r4 +; CHECK-NEXT: jo [[LABEL]] +; CHECK: mvhi 0(%r6), 0 +; CHECK: br %r14 + %res = load i32 *%resptr + %unused = call i8 *@strcpy(i8 *%dest, i8 *%src) + store i32 0, i32 *%storeptr + ret i32 %res +} diff --git a/test/MC/Disassembler/SystemZ/insns.txt b/test/MC/Disassembler/SystemZ/insns.txt index 5f2bc896e3f..c25bb9f9461 100644 --- a/test/MC/Disassembler/SystemZ/insns.txt +++ b/test/MC/Disassembler/SystemZ/insns.txt @@ -4678,6 +4678,18 @@ # CHECK: mviy 524287(%r15), 42 0xeb 0x2a 0xff 0xff 0x7f 0x52 +# CHECK: mvst %r0, %r0 +0xb2 0x55 0x00 0x00 + +# CHECK: mvst %r0, %r15 +0xb2 0x55 0x00 0x0f + +# CHECK: mvst %r15, %r0 +0xb2 0x55 0x00 0xf0 + +# CHECK: mvst %r7, %r8 +0xb2 0x55 0x00 0x78 + # CHECK: mxbr %f0, %f0 0xb3 0x4c 0x00 0x00 diff --git a/test/MC/SystemZ/insn-good.s b/test/MC/SystemZ/insn-good.s index a4529927e1a..da2b77e4eb7 100644 --- a/test/MC/SystemZ/insn-good.s +++ b/test/MC/SystemZ/insn-good.s @@ -5652,6 +5652,16 @@ mviy 524287(%r1), 42 mviy 524287(%r15), 42 +#CHECK: mvst %r0, %r0 # encoding: [0xb2,0x55,0x00,0x00] +#CHECK: mvst %r0, %r15 # encoding: [0xb2,0x55,0x00,0x0f] +#CHECK: mvst %r15, %r0 # encoding: [0xb2,0x55,0x00,0xf0] +#CHECK: mvst %r7, %r8 # encoding: [0xb2,0x55,0x00,0x78] + + mvst %r0,%r0 + mvst %r0,%r15 + mvst %r15,%r0 + mvst %r7,%r8 + #CHECK: mxbr %f0, %f0 # encoding: [0xb3,0x4c,0x00,0x00] #CHECK: mxbr %f0, %f13 # encoding: [0xb3,0x4c,0x00,0x0d] #CHECK: mxbr %f8, %f5 # encoding: [0xb3,0x4c,0x00,0x85] -- 2.34.1