From: Ahmed Bougacha Date: Fri, 3 Feb 2017 19:11:19 +0000 (+0000) Subject: [TLI] Robustize SDAG LibFunc proto checking by merging it into TLI. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=497ef8ab01901d65fdea1224a8dba5bb2ebddc56;p=llvm [TLI] Robustize SDAG LibFunc proto checking by merging it into TLI. This re-applies commit r292189, reverted in r292191. SelectionDAGBuilder recognizes libfuncs using some homegrown parameter type-checking. Use TLI instead, removing another heap of redundant code. This isn't strictly NFC, as the SDAG code was too lax. Concretely, this means changes are required to a few tests: - calling a non-variadic function via a variadic prototype isn't OK; it just happens to work on x86_64 (but not on, e.g., aarch64). - mempcpy has a size_t parameter; the SDAG code accepts any integer type, which meant using i32 on x86_64 worked. - a handful of SystemZ tests check the SDAG support for lax prototype checking: Ulrich agrees on removing them. I don't think it's worth supporting any of these (IMO) invalid testcases. Instead, fix them to be more meaningful. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@294028 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index b654086dd1a..cd1f002654c 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6054,20 +6054,13 @@ void SelectionDAGBuilder::processIntegerCallValue(const Instruction &I, setValue(&I, Value); } -/// visitMemCmpCall - See if we can lower a call to memcmp in an optimized form. -/// If so, return true and lower it, otherwise return false and it will be -/// lowered like a normal call. +/// See if we can lower a memcmp call into an optimized form. If so, return +/// true and lower it, otherwise return false and it will be lowered like a +/// normal call. +/// The caller already checked that \p I calls the appropriate LibFunc with a +/// correct prototype. bool SelectionDAGBuilder::visitMemCmpCall(const CallInst &I) { - // Verify that the prototype makes sense. int memcmp(void*,void*,size_t) - if (I.getNumArgOperands() != 3) - return false; - const Value *LHS = I.getArgOperand(0), *RHS = I.getArgOperand(1); - if (!LHS->getType()->isPointerTy() || !RHS->getType()->isPointerTy() || - !I.getArgOperand(2)->getType()->isIntegerTy() || - !I.getType()->isIntegerTy()) - return false; - const Value *Size = I.getArgOperand(2); const ConstantInt *CSize = dyn_cast(Size); if (CSize && CSize->getZExtValue() == 0) { @@ -6157,22 +6150,15 @@ bool SelectionDAGBuilder::visitMemCmpCall(const CallInst &I) { return false; } -/// visitMemChrCall -- See if we can lower a memchr call into an optimized -/// form. If so, return true and lower it, otherwise return false and it -/// will be lowered like a normal call. +/// See if we can lower a memchr call into an optimized form. If so, return +/// true and lower it, otherwise return false and it will be lowered like a +/// normal call. +/// The caller already checked that \p I calls the appropriate LibFunc with a +/// correct prototype. bool SelectionDAGBuilder::visitMemChrCall(const CallInst &I) { - // Verify that the prototype makes sense. void *memchr(void *, int, size_t) - if (I.getNumArgOperands() != 3) - return false; - const Value *Src = I.getArgOperand(0); const Value *Char = I.getArgOperand(1); const Value *Length = I.getArgOperand(2); - if (!Src->getType()->isPointerTy() || - !Char->getType()->isIntegerTy() || - !Length->getType()->isIntegerTy() || - !I.getType()->isPointerTy()) - return false; const SelectionDAGTargetInfo &TSI = DAG.getSelectionDAGInfo(); std::pair Res = @@ -6188,15 +6174,12 @@ bool SelectionDAGBuilder::visitMemChrCall(const CallInst &I) { return false; } -/// -/// visitMemPCpyCall -- lower a mempcpy call as a memcpy followed by code to -/// to adjust the dst pointer by the size of the copied memory. +/// See if we can lower a mempcpy call into an optimized form. If so, return +/// true and lower it, otherwise return false and it will be lowered like a +/// normal call. +/// The caller already checked that \p I calls the appropriate LibFunc with a +/// correct prototype. bool SelectionDAGBuilder::visitMemPCpyCall(const CallInst &I) { - - // Verify argument count: void *mempcpy(void *, const void *, size_t) - if (I.getNumArgOperands() != 3) - return false; - SDValue Dst = getValue(I.getArgOperand(0)); SDValue Src = getValue(I.getArgOperand(1)); SDValue Size = getValue(I.getArgOperand(2)); @@ -6231,19 +6214,13 @@ bool SelectionDAGBuilder::visitMemPCpyCall(const CallInst &I) { return true; } -/// 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. +/// See if we can lower a strcpy call into an optimized form. If so, return +/// true and lower it, otherwise return false and it will be lowered like a +/// normal call. +/// The caller already checked that \p I calls the appropriate LibFunc with a +/// correct prototype. 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 SelectionDAGTargetInfo &TSI = DAG.getSelectionDAGInfo(); std::pair Res = @@ -6260,19 +6237,13 @@ bool SelectionDAGBuilder::visitStrCpyCall(const CallInst &I, bool isStpcpy) { 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. +/// See if we can lower a strcmp call into an optimized form. If so, return +/// true and lower it, otherwise return false and it will be lowered like a +/// normal call. +/// The caller already checked that \p I calls the appropriate LibFunc with a +/// correct prototype. bool SelectionDAGBuilder::visitStrCmpCall(const CallInst &I) { - // Verify that the prototype makes sense. int strcmp(void*,void*) - 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()->isIntegerTy()) - return false; const SelectionDAGTargetInfo &TSI = DAG.getSelectionDAGInfo(); std::pair Res = @@ -6289,17 +6260,13 @@ bool SelectionDAGBuilder::visitStrCmpCall(const CallInst &I) { return false; } -/// visitStrLenCall -- See if we can lower a strlen call into an optimized -/// form. If so, return true and lower it, otherwise return false and it -/// will be lowered like a normal call. +/// See if we can lower a strlen call into an optimized form. If so, return +/// true and lower it, otherwise return false and it will be lowered like a +/// normal call. +/// The caller already checked that \p I calls the appropriate LibFunc with a +/// correct prototype. bool SelectionDAGBuilder::visitStrLenCall(const CallInst &I) { - // Verify that the prototype makes sense. size_t strlen(char *) - if (I.getNumArgOperands() != 1) - return false; - const Value *Arg0 = I.getArgOperand(0); - if (!Arg0->getType()->isPointerTy() || !I.getType()->isIntegerTy()) - return false; const SelectionDAGTargetInfo &TSI = DAG.getSelectionDAGInfo(); std::pair Res = @@ -6314,19 +6281,13 @@ bool SelectionDAGBuilder::visitStrLenCall(const CallInst &I) { return false; } -/// visitStrNLenCall -- See if we can lower a strnlen call into an optimized -/// form. If so, return true and lower it, otherwise return false and it -/// will be lowered like a normal call. +/// See if we can lower a strnlen call into an optimized form. If so, return +/// true and lower it, otherwise return false and it will be lowered like a +/// normal call. +/// The caller already checked that \p I calls the appropriate LibFunc with a +/// correct prototype. bool SelectionDAGBuilder::visitStrNLenCall(const CallInst &I) { - // Verify that the prototype makes sense. size_t strnlen(char *, size_t) - if (I.getNumArgOperands() != 2) - return false; - const Value *Arg0 = I.getArgOperand(0), *Arg1 = I.getArgOperand(1); - if (!Arg0->getType()->isPointerTy() || - !Arg1->getType()->isIntegerTy() || - !I.getType()->isIntegerTy()) - return false; const SelectionDAGTargetInfo &TSI = DAG.getSelectionDAGInfo(); std::pair Res = @@ -6342,16 +6303,15 @@ bool SelectionDAGBuilder::visitStrNLenCall(const CallInst &I) { return false; } -/// visitUnaryFloatCall - If a call instruction is a unary floating-point -/// operation (as expected), translate it to an SDNode with the specified opcode -/// and return true. +/// See if we can lower a unary floating-point operation into an SDNode with +/// the specified Opcode. If so, return true and lower it, otherwise return +/// false and it will be lowered like a normal call. +/// The caller already checked that \p I calls the appropriate LibFunc with a +/// correct prototype. bool SelectionDAGBuilder::visitUnaryFloatCall(const CallInst &I, unsigned Opcode) { - // Sanity check that it really is a unary floating-point call. - if (I.getNumArgOperands() != 1 || - !I.getArgOperand(0)->getType()->isFloatingPointTy() || - I.getType() != I.getArgOperand(0)->getType() || - !I.onlyReadsMemory()) + // We already checked this call's prototype; verify it doesn't modify errno. + if (!I.onlyReadsMemory()) return false; SDValue Tmp = getValue(I.getArgOperand(0)); @@ -6359,17 +6319,15 @@ bool SelectionDAGBuilder::visitUnaryFloatCall(const CallInst &I, return true; } -/// visitBinaryFloatCall - If a call instruction is a binary floating-point -/// operation (as expected), translate it to an SDNode with the specified opcode -/// and return true. +/// See if we can lower a binary floating-point operation into an SDNode with +/// the specified Opcode. If so, return true and lower it, otherwise return +/// false and it will be lowered like a normal call. +/// The caller already checked that \p I calls the appropriate LibFunc with a +/// correct prototype. bool SelectionDAGBuilder::visitBinaryFloatCall(const CallInst &I, unsigned Opcode) { - // Sanity check that it really is a binary floating-point call. - if (I.getNumArgOperands() != 2 || - !I.getArgOperand(0)->getType()->isFloatingPointTy() || - I.getType() != I.getArgOperand(0)->getType() || - I.getType() != I.getArgOperand(1)->getType() || - !I.onlyReadsMemory()) + // We already checked this call's prototype; verify it doesn't modify errno. + if (!I.onlyReadsMemory()) return false; SDValue Tmp0 = getValue(I.getArgOperand(0)); @@ -6411,18 +6369,16 @@ void SelectionDAGBuilder::visitCall(const CallInst &I) { // some reason. LibFunc Func; if (!I.isNoBuiltin() && !F->hasLocalLinkage() && F->hasName() && - LibInfo->getLibFunc(F->getName(), Func) && + LibInfo->getLibFunc(*F, Func) && LibInfo->hasOptimizedCodeGen(Func)) { switch (Func) { default: break; case LibFunc_copysign: case LibFunc_copysignf: case LibFunc_copysignl: - if (I.getNumArgOperands() == 2 && // Basic sanity checks. - I.getArgOperand(0)->getType()->isFloatingPointTy() && - I.getType() == I.getArgOperand(0)->getType() && - I.getType() == I.getArgOperand(1)->getType() && - I.onlyReadsMemory()) { + // We already checked this call's prototype; verify it doesn't modify + // errno. + if (I.onlyReadsMemory()) { SDValue LHS = getValue(I.getArgOperand(0)); SDValue RHS = getValue(I.getArgOperand(1)); setValue(&I, DAG.getNode(ISD::FCOPYSIGN, getCurSDLoc(), diff --git a/test/CodeGen/SystemZ/memchr-01.ll b/test/CodeGen/SystemZ/memchr-01.ll index f4d381b37f2..0cfca2af1e9 100644 --- a/test/CodeGen/SystemZ/memchr-01.ll +++ b/test/CodeGen/SystemZ/memchr-01.ll @@ -1,21 +1,57 @@ -; Test memchr using SRST, with a weird but usable prototype. +; Test memchr using SRST, with the correct prototype. ; -; RUN: llc < %s -mtriple=s390x-linux-gnu -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -mtriple=s390x-linux-gnu -no-integrated-as | FileCheck %s -declare i8 *@memchr(i8 *%src, i16 %char, i32 %len) +declare i8 *@memchr(i8 *%src, i32 %char, i64 %len) ; Test a simple forwarded call. -define i8 *@f1(i8 *%src, i16 %char, i32 %len) { +define i8 *@f1(i64 %len, i8 *%src, i32 %char) { ; CHECK-LABEL: f1: -; CHECK-DAG: lgr [[REG:%r[1-5]]], %r2 -; CHECK-DAG: algfr %r2, %r4 -; CHECK-DAG: llcr %r0, %r3 +; CHECK-DAG: agr %r2, %r3 +; CHECK-DAG: llcr %r0, %r4 ; CHECK: [[LABEL:\.[^:]*]]: -; CHECK: srst %r2, [[REG]] +; CHECK: srst %r2, %r3 ; CHECK-NEXT: jo [[LABEL]] ; CHECK: blr %r14 ; CHECK: lghi %r2, 0 ; CHECK: br %r14 - %res = call i8 *@memchr(i8 *%src, i16 %char, i32 %len) + %res = call i8 *@memchr(i8 *%src, i32 %char, i64 %len) ret i8 *%res } + +; Test a doubled call with no use of %r0 in between. There should be a +; single load of %r0. +define i8 *@f2(i8 *%src, i8 *%charptr, i64 %len) { +; CHECK-LABEL: f2: +; CHECK: llc %r0, 0(%r3) +; CHECK-NOT: %r0 +; CHECK: srst [[RES1:%r[1-5]]], %r2 +; CHECK-NOT: %r0 +; CHECK: srst %r2, [[RES1]] +; CHECK: br %r14 + %char = load volatile i8 , i8 *%charptr + %charext = zext i8 %char to i32 + %res1 = call i8 *@memchr(i8 *%src, i32 %charext, i64 %len) + %res2 = call i8 *@memchr(i8 *%res1, i32 %charext, i64 %len) + ret i8 *%res2 +} + +; Test a doubled call with a use of %r0 in between. %r0 must be loaded +; for each loop. +define i8 *@f3(i8 *%src, i8 *%charptr, i64 %len) { +; CHECK-LABEL: f3: +; CHECK: llc [[CHAR:%r[1-5]]], 0(%r3) +; CHECK: lr %r0, [[CHAR]] +; CHECK: srst [[RES1:%r[1-5]]], %r2 +; CHECK: lhi %r0, 0 +; CHECK: blah %r0 +; CHECK: lr %r0, [[CHAR]] +; CHECK: srst %r2, [[RES1]] +; CHECK: br %r14 + %char = load volatile i8 , i8 *%charptr + %charext = zext i8 %char to i32 + %res1 = call i8 *@memchr(i8 *%src, i32 %charext, i64 %len) + call void asm sideeffect "blah $0", "{r0}" (i32 0) + %res2 = call i8 *@memchr(i8 *%res1, i32 %charext, i64 %len) + ret i8 *%res2 +} diff --git a/test/CodeGen/SystemZ/memchr-02.ll b/test/CodeGen/SystemZ/memchr-02.ll deleted file mode 100644 index 0cfca2af1e9..00000000000 --- a/test/CodeGen/SystemZ/memchr-02.ll +++ /dev/null @@ -1,57 +0,0 @@ -; Test memchr using SRST, with the correct prototype. -; -; RUN: llc < %s -mtriple=s390x-linux-gnu -no-integrated-as | FileCheck %s - -declare i8 *@memchr(i8 *%src, i32 %char, i64 %len) - -; Test a simple forwarded call. -define i8 *@f1(i64 %len, i8 *%src, i32 %char) { -; CHECK-LABEL: f1: -; CHECK-DAG: agr %r2, %r3 -; CHECK-DAG: llcr %r0, %r4 -; CHECK: [[LABEL:\.[^:]*]]: -; CHECK: srst %r2, %r3 -; CHECK-NEXT: jo [[LABEL]] -; CHECK: blr %r14 -; CHECK: lghi %r2, 0 -; CHECK: br %r14 - %res = call i8 *@memchr(i8 *%src, i32 %char, i64 %len) - ret i8 *%res -} - -; Test a doubled call with no use of %r0 in between. There should be a -; single load of %r0. -define i8 *@f2(i8 *%src, i8 *%charptr, i64 %len) { -; CHECK-LABEL: f2: -; CHECK: llc %r0, 0(%r3) -; CHECK-NOT: %r0 -; CHECK: srst [[RES1:%r[1-5]]], %r2 -; CHECK-NOT: %r0 -; CHECK: srst %r2, [[RES1]] -; CHECK: br %r14 - %char = load volatile i8 , i8 *%charptr - %charext = zext i8 %char to i32 - %res1 = call i8 *@memchr(i8 *%src, i32 %charext, i64 %len) - %res2 = call i8 *@memchr(i8 *%res1, i32 %charext, i64 %len) - ret i8 *%res2 -} - -; Test a doubled call with a use of %r0 in between. %r0 must be loaded -; for each loop. -define i8 *@f3(i8 *%src, i8 *%charptr, i64 %len) { -; CHECK-LABEL: f3: -; CHECK: llc [[CHAR:%r[1-5]]], 0(%r3) -; CHECK: lr %r0, [[CHAR]] -; CHECK: srst [[RES1:%r[1-5]]], %r2 -; CHECK: lhi %r0, 0 -; CHECK: blah %r0 -; CHECK: lr %r0, [[CHAR]] -; CHECK: srst %r2, [[RES1]] -; CHECK: br %r14 - %char = load volatile i8 , i8 *%charptr - %charext = zext i8 %char to i32 - %res1 = call i8 *@memchr(i8 *%src, i32 %charext, i64 %len) - call void asm sideeffect "blah $0", "{r0}" (i32 0) - %res2 = call i8 *@memchr(i8 *%res1, i32 %charext, i64 %len) - ret i8 *%res2 -} diff --git a/test/CodeGen/SystemZ/memcmp-02.ll b/test/CodeGen/SystemZ/memcmp-02.ll deleted file mode 100644 index da11170def7..00000000000 --- a/test/CodeGen/SystemZ/memcmp-02.ll +++ /dev/null @@ -1,139 +0,0 @@ -; Test memcmp using CLC, with i64 results. -; -; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s - -declare i64 @memcmp(i8 *%src1, i8 *%src2, i64 %size) - -; Zero-length comparisons should be optimized away. -define i64 @f1(i8 *%src1, i8 *%src2) { -; CHECK-LABEL: f1: -; CHECK: lghi %r2, 0 -; CHECK: br %r14 - %res = call i64 @memcmp(i8 *%src1, i8 *%src2, i64 0) - ret i64 %res -} - -; Check a case where the result is used as an integer. -define i64 @f2(i8 *%src1, i8 *%src2) { -; CHECK-LABEL: f2: -; CHECK: clc 0(2,%r2), 0(%r3) -; CHECK: ipm [[REG:%r[0-5]]] -; CHECK: srl [[REG]], 28 -; CHECK: rll [[REG]], [[REG]], 31 -; CHECK: lgfr %r2, [[REG]] -; CHECK: br %r14 - %res = call i64 @memcmp(i8 *%src1, i8 *%src2, i64 2) - ret i64 %res -} - -; Check a case where the result is tested for equality. -define void @f3(i8 *%src1, i8 *%src2, i64 *%dest) { -; CHECK-LABEL: f3: -; CHECK: clc 0(3,%r2), 0(%r3) -; CHECK-NEXT: ber %r14 -; CHECK: br %r14 - %res = call i64 @memcmp(i8 *%src1, i8 *%src2, i64 3) - %cmp = icmp eq i64 %res, 0 - br i1 %cmp, label %exit, label %store - -store: - store i64 0, i64 *%dest - br label %exit - -exit: - ret void -} - -; Check a case where the result is tested for inequality. -define void @f4(i8 *%src1, i8 *%src2, i64 *%dest) { -; CHECK-LABEL: f4: -; CHECK: clc 0(4,%r2), 0(%r3) -; CHECK-NEXT: blhr %r14 -; CHECK: br %r14 -entry: - %res = call i64 @memcmp(i8 *%src1, i8 *%src2, i64 4) - %cmp = icmp ne i64 %res, 0 - br i1 %cmp, label %exit, label %store - -store: - store i64 0, i64 *%dest - br label %exit - -exit: - ret void -} - -; Check a case where the result is tested via slt. -define void @f5(i8 *%src1, i8 *%src2, i64 *%dest) { -; CHECK-LABEL: f5: -; CHECK: clc 0(5,%r2), 0(%r3) -; CHECK-NEXT: blr %r14 -; CHECK: br %r14 -entry: - %res = call i64 @memcmp(i8 *%src1, i8 *%src2, i64 5) - %cmp = icmp slt i64 %res, 0 - br i1 %cmp, label %exit, label %store - -store: - store i64 0, i64 *%dest - br label %exit - -exit: - ret void -} - -; Check a case where the result is tested for sgt. -define void @f6(i8 *%src1, i8 *%src2, i64 *%dest) { -; CHECK-LABEL: f6: -; CHECK: clc 0(6,%r2), 0(%r3) -; CHECK-NEXT: bhr %r14 -; CHECK: br %r14 -entry: - %res = call i64 @memcmp(i8 *%src1, i8 *%src2, i64 6) - %cmp = icmp sgt i64 %res, 0 - br i1 %cmp, label %exit, label %store - -store: - store i64 0, i64 *%dest - br label %exit - -exit: - ret void -} - -; Check the upper end of the CLC range. Here the result is used both as -; an integer and for branching. -define i64 @f7(i8 *%src1, i8 *%src2, i64 *%dest) { -; CHECK-LABEL: f7: -; CHECK: clc 0(256,%r2), 0(%r3) -; CHECK: ipm [[REG:%r[0-5]]] -; CHECK: srl [[REG]], 28 -; CHECK: rll [[REG]], [[REG]], 31 -; CHECK: lgfr %r2, [[REG]] -; CHECK: blr %r14 -; CHECK: br %r14 -entry: - %res = call i64 @memcmp(i8 *%src1, i8 *%src2, i64 256) - %cmp = icmp slt i64 %res, 0 - br i1 %cmp, label %exit, label %store - -store: - store i64 0, i64 *%dest - br label %exit - -exit: - ret i64 %res -} - -; 257 bytes needs two CLCs. -define i64 @f8(i8 *%src1, i8 *%src2) { -; CHECK-LABEL: f8: -; CHECK: clc 0(256,%r2), 0(%r3) -; CHECK: jlh [[LABEL:\..*]] -; CHECK: clc 256(1,%r2), 256(%r3) -; CHECK: [[LABEL]]: -; CHECK: ipm [[REG:%r[0-5]]] -; CHECK: br %r14 - %res = call i64 @memcmp(i8 *%src1, i8 *%src2, i64 257) - ret i64 %res -} diff --git a/test/CodeGen/SystemZ/strcmp-02.ll b/test/CodeGen/SystemZ/strcmp-02.ll deleted file mode 100644 index 99d7d9cfa69..00000000000 --- a/test/CodeGen/SystemZ/strcmp-02.ll +++ /dev/null @@ -1,72 +0,0 @@ -; Test strcmp using CLST, i64 version. -; -; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s - -declare i64 @strcmp(i8 *%src1, i8 *%src2) - -; Check a case where the result is used as an integer. -define i64 @f1(i8 *%src1, i8 *%src2) { -; CHECK-LABEL: f1: -; CHECK: lhi %r0, 0 -; CHECK: [[LABEL:\.[^:]*]]: -; CHECK: clst %r2, %r3 -; CHECK-NEXT: jo [[LABEL]] -; CHECK-NEXT: BB#{{[0-9]+}} -; CHECK-NEXT: ipm [[REG:%r[0-5]]] -; CHECK: srl [[REG]], 28 -; CHECK: rll [[REG]], [[REG]], 31 -; CHECK: lgfr %r2, [[REG]] -; CHECK: br %r14 - %res = call i64 @strcmp(i8 *%src1, i8 *%src2) - ret i64 %res -} - -; Check a case where the result is tested for equality. -define void @f2(i8 *%src1, i8 *%src2, i64 *%dest) { -; CHECK-LABEL: f2: -; CHECK: lhi %r0, 0 -; CHECK: [[LABEL:\.[^:]*]]: -; CHECK: clst %r2, %r3 -; CHECK-NEXT: jo [[LABEL]] -; CHECK-NEXT: BB#{{[0-9]+}} -; CHECK-NEXT: ber %r14 -; CHECK: br %r14 - %res = call i64 @strcmp(i8 *%src1, i8 *%src2) - %cmp = icmp eq i64 %res, 0 - br i1 %cmp, label %exit, label %store - -store: - store i64 0, i64 *%dest - br label %exit - -exit: - ret void -} - -; Test a case where the result is used both as an integer and for -; branching. -define i64 @f3(i8 *%src1, i8 *%src2, i64 *%dest) { -; CHECK-LABEL: f3: -; CHECK: lhi %r0, 0 -; CHECK: [[LABEL:\.[^:]*]]: -; CHECK: clst %r2, %r3 -; CHECK-NEXT: jo [[LABEL]] -; CHECK-NEXT: BB#{{[0-9]+}} -; CHECK-NEXT: ipm [[REG:%r[0-5]]] -; CHECK: srl [[REG]], 28 -; CHECK: rll [[REG]], [[REG]], 31 -; CHECK: lgfr %r2, [[REG]] -; CHECK: blr %r14 -; CHECK: br %r14 -entry: - %res = call i64 @strcmp(i8 *%src1, i8 *%src2) - %cmp = icmp slt i64 %res, 0 - br i1 %cmp, label %exit, label %store - -store: - store i64 0, i64 *%dest - br label %exit - -exit: - ret i64 %res -} diff --git a/test/CodeGen/SystemZ/strlen-02.ll b/test/CodeGen/SystemZ/strlen-02.ll deleted file mode 100644 index e1abbff4b4e..00000000000 --- a/test/CodeGen/SystemZ/strlen-02.ll +++ /dev/null @@ -1,39 +0,0 @@ -; Test strlen using SRST, i32 version. -; -; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s - -declare i32 @strlen(i8 *%src) -declare i32 @strnlen(i8 *%src, i32 %len) - -; Test strlen with an i32-based prototype. It would also be valid for -; the uses of %r3 and REG after the LGR to be swapped. -define i32 @f1(i32 %dummy, i8 *%src) { -; CHECK-LABEL: f1: -; CHECK-DAG: lhi %r0, 0 -; CHECK-DAG: lghi %r2, 0 -; CHECK-DAG: lgr [[REG:%r[145]]], %r3 -; CHECK: [[LABEL:\.[^:]*]]: -; CHECK-NEXT: srst %r2, [[REG]] -; CHECK-NEXT: jo [[LABEL]] -; CHECK-NEXT: BB#{{[0-9]+}} -; CHECK-NEXT: sgr %r2, %r3 -; CHECK: br %r14 - %res = call i32 @strlen(i8 *%src) - ret i32 %res -} - -; Test strnlen with an i32-based prototype. -define i32 @f2(i32 zeroext %len, i8 *%src) { -; CHECK-LABEL: f2: -; CHECK-DAG: agr %r2, %r3 -; CHECK-DAG: lhi %r0, 0 -; CHECK-DAG: lgr [[REG:%r[145]]], %r3 -; CHECK: [[LABEL:\.[^:]*]]: -; CHECK-NEXT: srst %r2, [[REG]] -; CHECK-NEXT: jo [[LABEL]] -; CHECK-NEXT: BB#{{[0-9]+}} -; CHECK-NEXT: sgr %r2, %r3 -; CHECK: br %r14 - %res = call i32 @strnlen(i8 *%src, i32 %len) - ret i32 %res -} diff --git a/test/CodeGen/X86/memcmp.ll b/test/CodeGen/X86/memcmp.ll index 6a51d60f636..02e2e1be547 100644 --- a/test/CodeGen/X86/memcmp.ll +++ b/test/CodeGen/X86/memcmp.ll @@ -7,11 +7,11 @@ @.str = private constant [23 x i8] c"fooooooooooooooooooooo\00", align 1 ; <[23 x i8]*> [#uses=1] -declare i32 @memcmp(...) +declare i32 @memcmp(i8*, i8*, i64) define void @memcmp2(i8* %X, i8* %Y, i32* nocapture %P) nounwind { entry: - %0 = tail call i32 (...) @memcmp(i8* %X, i8* %Y, i32 2) nounwind ; [#uses=1] + %0 = tail call i32 @memcmp(i8* %X, i8* %Y, i64 2) nounwind ; [#uses=1] %1 = icmp eq i32 %0, 0 ; [#uses=1] br i1 %1, label %return, label %bb @@ -30,7 +30,7 @@ return: ; preds = %entry define void @memcmp2a(i8* %X, i32* nocapture %P) nounwind { entry: - %0 = tail call i32 (...) @memcmp(i8* %X, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 1), i32 2) nounwind ; [#uses=1] + %0 = tail call i32 @memcmp(i8* %X, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 1), i64 2) nounwind ; [#uses=1] %1 = icmp eq i32 %0, 0 ; [#uses=1] br i1 %1, label %return, label %bb @@ -47,7 +47,7 @@ return: ; preds = %entry define void @memcmp2nb(i8* %X, i8* %Y, i32* nocapture %P) nounwind { entry: - %0 = tail call i32 (...) @memcmp(i8* %X, i8* %Y, i32 2) nounwind nobuiltin ; [#uses=1] + %0 = tail call i32 @memcmp(i8* %X, i8* %Y, i64 2) nounwind nobuiltin ; [#uses=1] %1 = icmp eq i32 %0, 0 ; [#uses=1] br i1 %1, label %return, label %bb @@ -63,7 +63,7 @@ return: ; preds = %entry define void @memcmp4(i8* %X, i8* %Y, i32* nocapture %P) nounwind { entry: - %0 = tail call i32 (...) @memcmp(i8* %X, i8* %Y, i32 4) nounwind ; [#uses=1] + %0 = tail call i32 @memcmp(i8* %X, i8* %Y, i64 4) nounwind ; [#uses=1] %1 = icmp eq i32 %0, 0 ; [#uses=1] br i1 %1, label %return, label %bb @@ -80,7 +80,7 @@ return: ; preds = %entry define void @memcmp4a(i8* %X, i32* nocapture %P) nounwind { entry: - %0 = tail call i32 (...) @memcmp(i8* %X, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 1), i32 4) nounwind ; [#uses=1] + %0 = tail call i32 @memcmp(i8* %X, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 1), i64 4) nounwind ; [#uses=1] %1 = icmp eq i32 %0, 0 ; [#uses=1] br i1 %1, label %return, label %bb @@ -96,7 +96,7 @@ return: ; preds = %entry define void @memcmp8(i8* %X, i8* %Y, i32* nocapture %P) nounwind { entry: - %0 = tail call i32 (...) @memcmp(i8* %X, i8* %Y, i32 8) nounwind ; [#uses=1] + %0 = tail call i32 @memcmp(i8* %X, i8* %Y, i64 8) nounwind ; [#uses=1] %1 = icmp eq i32 %0, 0 ; [#uses=1] br i1 %1, label %return, label %bb @@ -113,7 +113,7 @@ return: ; preds = %entry define void @memcmp8a(i8* %X, i32* nocapture %P) nounwind { entry: - %0 = tail call i32 (...) @memcmp(i8* %X, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 0), i32 8) nounwind ; [#uses=1] + %0 = tail call i32 @memcmp(i8* %X, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 0), i64 8) nounwind ; [#uses=1] %1 = icmp eq i32 %0, 0 ; [#uses=1] br i1 %1, label %return, label %bb diff --git a/test/CodeGen/X86/mempcpy-32.ll b/test/CodeGen/X86/mempcpy-32.ll new file mode 100644 index 00000000000..108442f6b64 --- /dev/null +++ b/test/CodeGen/X86/mempcpy-32.ll @@ -0,0 +1,20 @@ +; RUN: llc < %s -mtriple=i686-unknown-linux -O2 | FileCheck %s + +; This tests the i686 lowering of mempcpy. +; Also see mempcpy.ll + +@G = common global i8* null, align 8 + +; CHECK-LABEL: RET_MEMPCPY: +; CHECK: movl [[REG:%e[a-z0-9]+]], {{.*}}G +; CHECK: calll {{.*}}memcpy +; CHECK: movl [[REG]], %eax +; +define i8* @RET_MEMPCPY(i8* %DST, i8* %SRC, i32 %N) { + %add.ptr = getelementptr inbounds i8, i8* %DST, i32 %N + store i8* %add.ptr, i8** @G, align 8 + %call = tail call i8* @mempcpy(i8* %DST, i8* %SRC, i32 %N) + ret i8* %call +} + +declare i8* @mempcpy(i8*, i8*, i32) diff --git a/test/CodeGen/X86/mempcpy.ll b/test/CodeGen/X86/mempcpy.ll index 1c737b64402..f8db255c1a4 100644 --- a/test/CodeGen/X86/mempcpy.ll +++ b/test/CodeGen/X86/mempcpy.ll @@ -1,5 +1,4 @@ ; RUN: llc < %s -mtriple=x86_64-unknown-linux -O2 | FileCheck %s -; RUN: llc < %s -mtriple=i686-unknown-linux -O2 | FileCheck %s ; This test checks that: ; (1) mempcpy is lowered as memcpy, and @@ -11,12 +10,15 @@ ; the first instance to be reused as the return value. This allows the check for ; (2) to be expressed as verifying that the MOV to store DST+N to G and ; the MOV to copy DST+N to %rax use the same source register. + +; Also see mempcpy-32.ll + @G = common global i8* null, align 8 ; CHECK-LABEL: RET_MEMPCPY: -; CHECK: mov{{.*}} [[REG:%[er][a-z0-9]+]], {{.*}}G -; CHECK: call{{.*}} {{.*}}memcpy -; CHECK: mov{{.*}} [[REG]], %{{[er]}}ax +; CHECK: movq [[REG:%r[a-z0-9]+]], {{.*}}G +; CHECK: callq {{.*}}memcpy +; CHECK: movq [[REG]], %rax ; define i8* @RET_MEMPCPY(i8* %DST, i8* %SRC, i64 %N) { %add.ptr = getelementptr inbounds i8, i8* %DST, i64 %N