mirror of
https://github.com/RPCSX/llvm.git
synced 2025-02-01 01:54:21 +00:00
Fix for Bug 26903, adds support to inline __builtin_mempcpy
Patch by Sunita Marathe Differential Revision: http://reviews.llvm.org/D21920 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@275284 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
c0ba7ba967
commit
c949e6e642
@ -734,6 +734,9 @@ TLI_DEFINE_STRING_INTERNAL("memcpy")
|
||||
/// void *memmove(void *s1, const void *s2, size_t n);
|
||||
TLI_DEFINE_ENUM_INTERNAL(memmove)
|
||||
TLI_DEFINE_STRING_INTERNAL("memmove")
|
||||
/// void *mempcpy(void *s1, const void *s2, size_t n);
|
||||
TLI_DEFINE_ENUM_INTERNAL(mempcpy)
|
||||
TLI_DEFINE_STRING_INTERNAL("mempcpy")
|
||||
// void *memrchr(const void *s, int c, size_t n);
|
||||
TLI_DEFINE_ENUM_INTERNAL(memrchr)
|
||||
TLI_DEFINE_STRING_INTERNAL("memrchr")
|
||||
|
@ -251,7 +251,7 @@ public:
|
||||
case LibFunc::exp2: case LibFunc::exp2f: case LibFunc::exp2l:
|
||||
case LibFunc::memcmp: case LibFunc::strcmp: case LibFunc::strcpy:
|
||||
case LibFunc::stpcpy: case LibFunc::strlen: case LibFunc::strnlen:
|
||||
case LibFunc::memchr:
|
||||
case LibFunc::memchr: case LibFunc::mempcpy:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -642,6 +642,7 @@ bool TargetLibraryInfoImpl::isValidProtoForLibFunc(const FunctionType &FTy,
|
||||
return false;
|
||||
// fallthrough
|
||||
case LibFunc::memcpy:
|
||||
case LibFunc::mempcpy:
|
||||
case LibFunc::memmove:
|
||||
return (NumParams == 3 && FTy.getReturnType() == FTy.getParamType(0) &&
|
||||
FTy.getParamType(0)->isPointerTy() &&
|
||||
|
@ -6039,6 +6039,48 @@ 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.
|
||||
bool SelectionDAGBuilder::visitMemPCpyCall(const CallInst &I) {
|
||||
|
||||
unsigned int NumOperands = I.getNumArgOperands();
|
||||
if (NumOperands < 3 || NumOperands > 5)
|
||||
return false;
|
||||
|
||||
SDValue Dst = getValue(I.getArgOperand(0));
|
||||
SDValue Src = getValue(I.getArgOperand(1));
|
||||
SDValue Size = getValue(I.getArgOperand(2));
|
||||
|
||||
unsigned Align = 0;
|
||||
if (NumOperands >= 4)
|
||||
Align = cast<ConstantInt>(I.getArgOperand(3))->getZExtValue();
|
||||
if (!Align)
|
||||
Align = 1; // @llvm.memcpy defines 0 and 1 to both mean no alignment.
|
||||
|
||||
bool isVol = false;
|
||||
if (NumOperands == 5)
|
||||
isVol = cast<ConstantInt>(I.getArgOperand(4))->getZExtValue();
|
||||
|
||||
SDLoc sdl = getCurSDLoc();
|
||||
// In the mempcpy context we need to pass in a false value for isTailCall
|
||||
// because the return pointer needs to be adjusted by the size of
|
||||
// the copied memory.
|
||||
SDValue MC = DAG.getMemcpy(getRoot(), sdl, Dst, Src, Size, Align, isVol,
|
||||
false, /*isTailCall=*/false,
|
||||
MachinePointerInfo(I.getArgOperand(0)),
|
||||
MachinePointerInfo(I.getArgOperand(1)));
|
||||
assert(MC.getNode() != nullptr &&
|
||||
"** memcpy should not be lowered as TailCall in mempcpy context **");
|
||||
DAG.setRoot(MC);
|
||||
|
||||
// Adjust return pointer to point just past the last dst byte.
|
||||
SDValue DstPlusSize = DAG.getNode(ISD::ADD, sdl, Dst.getValueType(),
|
||||
Dst, Size);
|
||||
setValue(&I, DstPlusSize);
|
||||
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.
|
||||
@ -6329,6 +6371,10 @@ void SelectionDAGBuilder::visitCall(const CallInst &I) {
|
||||
if (visitMemCmpCall(I))
|
||||
return;
|
||||
break;
|
||||
case LibFunc::mempcpy:
|
||||
if (visitMemPCpyCall(I))
|
||||
return;
|
||||
break;
|
||||
case LibFunc::memchr:
|
||||
if (visitMemChrCall(I))
|
||||
return;
|
||||
|
@ -885,6 +885,7 @@ private:
|
||||
void visitPHI(const PHINode &I);
|
||||
void visitCall(const CallInst &I);
|
||||
bool visitMemCmpCall(const CallInst &I);
|
||||
bool visitMemPCpyCall(const CallInst &I);
|
||||
bool visitMemChrCall(const CallInst &I);
|
||||
bool visitStrCpyCall(const CallInst &I, bool isStpcpy);
|
||||
bool visitStrCmpCall(const CallInst &I);
|
||||
|
@ -250,6 +250,7 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
|
||||
Changed |= setDoesNotCapture(F, 2);
|
||||
return Changed;
|
||||
case LibFunc::memcpy:
|
||||
case LibFunc::mempcpy:
|
||||
case LibFunc::memccpy:
|
||||
case LibFunc::memmove:
|
||||
Changed |= setDoesNotThrow(F);
|
||||
|
16
test/CodeGen/Generic/mempcpy_call.ll
Normal file
16
test/CodeGen/Generic/mempcpy_call.ll
Normal file
@ -0,0 +1,16 @@
|
||||
; RUN: llc < %s -O2 | FileCheck %s
|
||||
|
||||
; This test just checks that mempcpy is lowered as memcpy.
|
||||
; The test to check that the return value of mempcpy is the dst pointer adjusted
|
||||
; by the copy size is done by Codegen/X86/mempcpy_ret_val.ll
|
||||
|
||||
; CHECK-LABEL: CALL_MEMPCPY:
|
||||
; CHECK: callq memcpy
|
||||
;
|
||||
define void @CALL_MEMPCPY(i8* %DST, i8* %SRC, i64 %N) {
|
||||
entry:
|
||||
%call = tail call i8* @mempcpy(i8* %DST, i8* %SRC, i64 %N)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i8* @mempcpy(i8*, i8*, i64)
|
28
test/CodeGen/X86/mempcpy_ret_val.ll
Normal file
28
test/CodeGen/X86/mempcpy_ret_val.ll
Normal file
@ -0,0 +1,28 @@
|
||||
; RUN: llc < %s -O2 | FileCheck %s
|
||||
|
||||
@G = common global i8* null, align 8
|
||||
|
||||
; This test checks that:
|
||||
; (1) mempcpy is lowered as memcpy, and
|
||||
; (2) its return value is DST+N i.e. the dst pointer adjusted by the copy size.
|
||||
; To keep the testing of (2) independent of the exact instructions used to
|
||||
; adjust the dst pointer, DST+N is explicitly computed and stored to a global
|
||||
; variable G before the mempcpy call. This instance of DST+N causes the repeat
|
||||
; DST+N done in the context of the return value of mempcpy to be redundant, and
|
||||
; 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.
|
||||
|
||||
; CHECK-LABEL: RET_MEMPCPY:
|
||||
; 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
|
||||
store i8* %add.ptr, i8** @G, align 8
|
||||
%call = tail call i8* @mempcpy(i8* %DST, i8* %SRC, i64 %N)
|
||||
ret i8* %call
|
||||
}
|
||||
|
||||
declare i8* @mempcpy(i8*, i8*, i64)
|
@ -499,6 +499,9 @@ declare i32 @memcmp(i8*, i8*, i64)
|
||||
; CHECK: declare i8* @memcpy(i8*, i8* nocapture readonly, i64) [[G0]]
|
||||
declare i8* @memcpy(i8*, i8*, i64)
|
||||
|
||||
; CHECK: declare i8* @mempcpy(i8*, i8* nocapture readonly, i64) [[G0]]
|
||||
declare i8* @mempcpy(i8*, i8*, i64)
|
||||
|
||||
; CHECK: declare i8* @memmove(i8*, i8* nocapture readonly, i64) [[G0]]
|
||||
declare i8* @memmove(i8*, i8*, i64)
|
||||
|
||||
|
@ -480,6 +480,9 @@ declare void @memcmp(...)
|
||||
; CHECK: declare void @memcpy(...)
|
||||
declare void @memcpy(...)
|
||||
|
||||
; CHECK: declare void @mempcpy(...)
|
||||
declare void @mempcpy(...)
|
||||
|
||||
; CHECK: declare void @memmove(...)
|
||||
declare void @memmove(...)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user