mirror of
https://github.com/RPCSX/llvm.git
synced 2025-04-02 16:21:36 +00:00
[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
This commit is contained in:
parent
dce4987e9b
commit
497ef8ab01
@ -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<ConstantInt>(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<SDValue, SDValue> 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<SDValue, SDValue> 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<SDValue, SDValue> 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<SDValue, SDValue> 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<SDValue, SDValue> 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(),
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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 ; <i32> [#uses=1]
|
||||
%0 = tail call i32 @memcmp(i8* %X, i8* %Y, i64 2) nounwind ; <i32> [#uses=1]
|
||||
%1 = icmp eq i32 %0, 0 ; <i1> [#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 ; <i32> [#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 ; <i32> [#uses=1]
|
||||
%1 = icmp eq i32 %0, 0 ; <i1> [#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 ; <i32> [#uses=1]
|
||||
%0 = tail call i32 @memcmp(i8* %X, i8* %Y, i64 2) nounwind nobuiltin ; <i32> [#uses=1]
|
||||
%1 = icmp eq i32 %0, 0 ; <i1> [#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 ; <i32> [#uses=1]
|
||||
%0 = tail call i32 @memcmp(i8* %X, i8* %Y, i64 4) nounwind ; <i32> [#uses=1]
|
||||
%1 = icmp eq i32 %0, 0 ; <i1> [#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 ; <i32> [#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 ; <i32> [#uses=1]
|
||||
%1 = icmp eq i32 %0, 0 ; <i1> [#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 ; <i32> [#uses=1]
|
||||
%0 = tail call i32 @memcmp(i8* %X, i8* %Y, i64 8) nounwind ; <i32> [#uses=1]
|
||||
%1 = icmp eq i32 %0, 0 ; <i1> [#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 ; <i32> [#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 ; <i32> [#uses=1]
|
||||
%1 = icmp eq i32 %0, 0 ; <i1> [#uses=1]
|
||||
br i1 %1, label %return, label %bb
|
||||
|
||||
|
20
test/CodeGen/X86/mempcpy-32.ll
Normal file
20
test/CodeGen/X86/mempcpy-32.ll
Normal file
@ -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)
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user