blob: a42232b42a33643894b41a34227aae8320cd3c42 [file] [log] [blame]
/* Optimized memset implementation for PowerPC64/POWER8.
Copyright (C) 2014-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <sysdep.h>
#define MTVSRD_V1_R4 .long 0x7c240166 /* mtvsrd v1,r4 */
/* void * [r3] memset (void *s [r3], int c [r4], size_t n [r5]));
Returns 's'. */
#ifndef MEMSET
# define MEMSET memset
#endif
/* No need to use .machine power8 since mtvsrd is already
handled by the define. It avoid breakage on binutils
that does not support this machine specifier. */
.machine power7
ENTRY_TOCLESS (MEMSET, 5)
CALL_MCOUNT 3
L(_memset):
cmpldi cr7,r5,31
neg r0,r3
mr r10,r3
insrdi r4,r4,8,48
insrdi r4,r4,16,32 /* Replicate byte to word. */
ble cr7,L(write_LT_32)
andi. r11,r10,15 /* Check alignment of DST. */
insrdi r4,r4,32,0 /* Replicate word to double word. */
beq L(big_aligned)
mtocrf 0x01,r0
clrldi r0,r0,60
/* Get DST aligned to 16 bytes. */
1: bf 31,2f
stb r4,0(r10)
addi r10,r10,1
2: bf 30,4f
sth r4,0(r10)
addi r10,r10,2
4: bf 29,8f
stw r4,0(r10)
addi r10,r10,4
8: bf 28,16f
std r4,0(r10)
addi r10,r10,8
16: subf r5,r0,r5
.align 4
L(big_aligned):
/* For sizes larger than 255 two possible paths:
- if constant is '0', zero full cache lines with dcbz
- otherwise uses vector instructions. */
cmpldi cr5,r5,255
dcbtst 0,r10
cmpldi cr6,r4,0
crand 27,26,21
bt 27,L(huge_dcbz)
bge cr5,L(huge_vector)
/* Size between 32 and 255 bytes with constant different than 0, use
doubleword store instruction to achieve best throughput. */
srdi r8,r5,5
clrldi r11,r5,59
cmpldi cr6,r11,0
cmpdi r8,0
beq L(tail_bytes)
mtctr r8
/* Main aligned write loop, writes 32-bytes at a time. */
.align 4
L(big_loop):
std r4,0(r10)
std r4,8(r10)
std r4,16(r10)
std r4,24(r10)
addi r10,r10,32
bdz L(tail_bytes)
std r4,0(r10)
std r4,8(r10)
std r4,16(r10)
std r4,24(r10)
addi r10,10,32
bdnz L(big_loop)
b L(tail_bytes)
/* Write remaining 1~31 bytes. */
.align 4
L(tail_bytes):
beqlr cr6
srdi r7,r11,4
clrldi r8,r11,60
mtocrf 0x01,r7
.align 4
bf 31,8f
std r4,0(r10)
std r4,8(r10)
addi r10,r10,16
.align 4
8: mtocrf 0x1,r8
bf 28,4f
std r4,0(r10)
addi r10,r10,8
.align 4
4: bf 29,2f
stw 4,0(10)
addi 10,10,4
.align 4
2: bf 30,1f
sth 4,0(10)
addi 10,10,2
.align 4
1: bflr 31
stb 4,0(10)
blr
/* Size larger than 255 bytes with constant different than 0, use
vector instruction to achieve best throughput. */
L(huge_vector):
/* Replicate set byte to quadword in VMX register. */
MTVSRD_V1_R4
xxpermdi 32,v0,v1,0
vspltb v2,v0,15
/* Main aligned write loop: 128 bytes at a time. */
li r6,16
li r7,32
li r8,48
mtocrf 0x02,r5
srdi r12,r5,7
cmpdi r12,0
beq L(aligned_tail)
mtctr r12
b L(aligned_128loop)
.align 4
L(aligned_128loop):
stvx v2,0,r10
stvx v2,r10,r6
stvx v2,r10,r7
stvx v2,r10,r8
addi r10,r10,64
stvx v2,0,r10
stvx v2,r10,r6
stvx v2,r10,r7
stvx v2,r10,r8
addi r10,r10,64
bdnz L(aligned_128loop)
/* Write remaining 1~127 bytes. */
L(aligned_tail):
mtocrf 0x01,r5
bf 25,32f
stvx v2,0,r10
stvx v2,r10,r6
stvx v2,r10,r7
stvx v2,r10,r8
addi r10,r10,64
32: bf 26,16f
stvx v2,0,r10
stvx v2,r10,r6
addi r10,r10,32
16: bf 27,8f
stvx v2,0,r10
addi r10,r10,16
8: bf 28,4f
std r4,0(r10)
addi r10,r10,8
/* Copies 4~7 bytes. */
4: bf 29,L(tail2)
stw r4,0(r10)
bf 30,L(tail5)
sth r4,4(r10)
bflr 31
stb r4,6(r10)
/* Return original DST pointer. */
blr
/* Special case when value is 0 and we have a long length to deal
with. Use dcbz to zero out a full cacheline of 128 bytes at a time.
Before using dcbz though, we need to get the destination 128-byte
aligned. */
.align 4
L(huge_dcbz):
andi. r11,r10,127
neg r0,r10
beq L(huge_dcbz_aligned)
clrldi r0,r0,57
subf r5,r0,r5
srdi r0,r0,3
mtocrf 0x01,r0
/* Write 1~128 bytes until DST is aligned to 128 bytes. */
8: bf 28,4f
std r4,0(r10)
std r4,8(r10)
std r4,16(r10)
std r4,24(r10)
std r4,32(r10)
std r4,40(r10)
std r4,48(r10)
std r4,56(r10)
addi r10,r10,64
.align 4
4: bf 29,2f
std r4,0(r10)
std r4,8(r10)
std r4,16(r10)
std r4,24(r10)
addi r10,r10,32
.align 4
2: bf 30,1f
std r4,0(r10)
std r4,8(r10)
addi r10,r10,16
.align 4
1: bf 31,L(huge_dcbz_aligned)
std r4,0(r10)
addi r10,r10,8
L(huge_dcbz_aligned):
/* Setup dcbz unroll offsets and count numbers. */
srdi r8,r5,9
clrldi r11,r5,55
cmpldi cr6,r11,0
li r9,128
cmpdi r8,0
beq L(huge_tail)
li r7,256
li r6,384
mtctr r8
.align 4
L(huge_loop):
/* Sets 512 bytes to zero in each iteration, the loop unrolling shows
a throughput boost for large sizes (2048 bytes or higher). */
dcbz 0,r10
dcbz r9,r10
dcbz r7,r10
dcbz r6,r10
addi r10,r10,512
bdnz L(huge_loop)
beqlr cr6
L(huge_tail):
srdi r6,r11,8
srdi r7,r11,4
clrldi r8,r11,4
cmpldi cr6,r8,0
mtocrf 0x01,r6
beq cr6,L(tail)
/* We have 1~511 bytes remaining. */
.align 4
32: bf 31,16f
dcbz 0,r10
dcbz r9,r10
addi r10,r10,256
.align 4
16: mtocrf 0x01,r7
bf 28,8f
dcbz 0,r10
addi r10,r10,128
.align 4
8: bf 29,4f
std r4,0(r10)
std r4,8(r10)
std r4,16(r10)
std r4,24(r10)
std r4,32(r10)
std r4,40(r10)
std r4,48(r10)
std r4,56(r10)
addi r10,r10,64
.align 4
4: bf 30,2f
std r4,0(r10)
std r4,8(r10)
std r4,16(r10)
std r4,24(r10)
addi r10,r10,32
.align 4
2: bf 31,L(tail)
std r4,0(r10)
std r4,8(r10)
addi r10,r10,16
.align 4
/* Remaining 1~15 bytes. */
L(tail):
mtocrf 0x01,r8
.align
8: bf 28,4f
std r4,0(r10)
addi r10,r10,8
.align 4
4: bf 29,2f
stw r4,0(r10)
addi r10,r10,4
.align 4
2: bf 30,1f
sth r4,0(r10)
addi r10,r10,2
.align 4
1: bflr 31
stb r4,0(r10)
blr
/* Handle short copies of 0~31 bytes. Best throughput is achieved
by just unrolling all operations. */
.align 4
L(write_LT_32):
cmpldi cr6,5,8
mtocrf 0x01,r5
ble cr6,L(write_LE_8)
/* At least 9 bytes to go. */
neg r8,r4
andi. r0,r8,3
cmpldi cr1,r5,16
beq L(write_LT_32_aligned)
/* Force 4-byte alignment for SRC. */
mtocrf 0x01,r0
subf r5,r0,r5
2: bf 30,1f
/* Use stb instead of sth because it doesn't generate
alignment interrupts on cache-inhibited storage. */
stb r4,0(r10)
stb r4,1(r10)
addi r10,r10,2
1: bf 31,L(end_4bytes_alignment)
stb r4,0(r10)
addi r10,r10,1
.align 4
L(end_4bytes_alignment):
cmpldi cr1,r5,16
mtocrf 0x01,r5
L(write_LT_32_aligned):
blt cr1,8f
stw r4,0(r10)
stw r4,4(r10)
stw r4,8(r10)
stw r4,12(r10)
addi r10,r10,16
8: bf 28,L(tail4)
stw r4,0(r10)
stw r4,4(r10)
addi r10,r10,8
.align 4
/* Copies 4~7 bytes. */
L(tail4):
bf 29,L(tail2)
stw r4,0(r10)
bf 30,L(tail5)
sth r4,4(r10)
bflr 31
stb r4,6(r10)
blr
.align 4
/* Copies 2~3 bytes. */
L(tail2):
bf 30,1f
sth r4,0(r10)
bflr 31
stb r4,2(r10)
blr
.align 4
L(tail5):
bflr 31
stb r4,4(r10)
blr
.align 4
1: bflr 31
stb r4,0(r10)
blr
/* Handles copies of 0~8 bytes. */
.align 4
L(write_LE_8):
bne cr6,L(LE7_tail4)
/* If input is word aligned, use stw, else use stb. */
andi. r0,r10,3
bne L(8_unalign)
stw r4,0(r10)
stw r4,4(r10)
blr
/* Unaligned input and size is 8. */
.align 4
L(8_unalign):
andi. r0,r10,1
beq L(8_hwalign)
stb r4,0(r10)
sth r4,1(r10)
sth r4,3(r10)
sth r4,5(r10)
stb r4,7(r10)
blr
/* Halfword aligned input and size is 8. */
.align 4
L(8_hwalign):
sth r4,0(r10)
sth r4,2(r10)
sth r4,4(r10)
sth r4,6(r10)
blr
.align 4
/* Copies 4~7 bytes. */
L(LE7_tail4):
/* Use stb instead of sth because it doesn't generate
alignment interrupts on cache-inhibited storage. */
bf 29,L(LE7_tail2)
stb r4,0(r10)
stb r4,1(r10)
stb r4,2(r10)
stb r4,3(r10)
bf 30,L(LE7_tail5)
stb r4,4(r10)
stb r4,5(r10)
bflr 31
stb r4,6(r10)
blr
.align 4
/* Copies 2~3 bytes. */
L(LE7_tail2):
bf 30,1f
stb r4,0(r10)
stb r4,1(r10)
bflr 31
stb r4,2(r10)
blr
.align 4
L(LE7_tail5):
bflr 31
stb r4,4(r10)
blr
.align 4
1: bflr 31
stb r4,0(r10)
blr
END_GEN_TB (MEMSET,TB_TOCLESS)
libc_hidden_builtin_def (memset)
/* Copied from bzero.S to prevent the linker from inserting a stub
between bzero and memset. */
ENTRY_TOCLESS (__bzero)
CALL_MCOUNT 3
mr r5,r4
li r4,0
b L(_memset)
END (__bzero)
#ifndef __bzero
weak_alias (__bzero, bzero)
#endif