5de29b
# commit db9b4570c5dc550074140ac1d1677077fba29a26
5de29b
# Author: Alan Modra <amodra@gmail.com>
5de29b
# Date:   Sat Aug 17 18:40:11 2013 +0930
5de29b
# 
5de29b
#     PowerPC LE strlen
5de29b
#     http://sourceware.org/ml/libc-alpha/2013-08/msg00097.html
5de29b
#     
5de29b
#     This is the first of nine patches adding little-endian support to the
5de29b
#     existing optimised string and memory functions.  I did spend some
5de29b
#     time with a power7 simulator looking at cycle by cycle behaviour for
5de29b
#     memchr, but most of these patches have not been run on cpu simulators
5de29b
#     to check that we are going as fast as possible.  I'm sure PowerPC can
5de29b
#     do better.  However, the little-endian support mostly leaves main
5de29b
#     loops unchanged, so I'm banking on previous authors having done a
5de29b
#     good job on big-endian..  As with most code you stare at long enough,
5de29b
#     I found some improvements for big-endian too.
5de29b
#     
5de29b
#     Little-endian support for strlen.  Like most of the string functions,
5de29b
#     I leave the main word or multiple-word loops substantially unchanged,
5de29b
#     just needing to modify the tail.
5de29b
#     
5de29b
#     Removing the branch in the power7 functions is just a tidy.  .align
5de29b
#     produces a branch anyway.  Modifying regs in the non-power7 functions
5de29b
#     is to suit the new little-endian tail.
5de29b
#     
5de29b
#         * sysdeps/powerpc/powerpc64/power7/strlen.S (strlen): Add little-endian
5de29b
#         support.  Don't branch over align.
5de29b
#         * sysdeps/powerpc/powerpc32/power7/strlen.S: Likewise.
5de29b
#         * sysdeps/powerpc/powerpc64/strlen.S (strlen): Add little-endian support.
5de29b
#         Rearrange tmp reg use to suit.  Comment.
5de29b
#         * sysdeps/powerpc/powerpc32/strlen.S: Likewise.
5de29b
# 
12745e
diff -urN glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/power7/strlen.S glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/power7/strlen.S
12745e
--- glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/power7/strlen.S	2014-05-28 12:28:44.000000000 -0500
12745e
+++ glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/power7/strlen.S	2014-05-28 12:28:45.000000000 -0500
5de29b
@@ -31,7 +31,11 @@
5de29b
 	li	r0,0	      /* Word with null chars to use with cmpb.  */
5de29b
 	li	r5,-1	      /* MASK = 0xffffffffffffffff.  */
5de29b
 	lwz	r12,0(r4)     /* Load word from memory.  */
5de29b
+#ifdef __LITTLE_ENDIAN__
5de29b
+	slw	r5,r5,r6
5de29b
+#else
5de29b
 	srw	r5,r5,r6      /* MASK = MASK >> padding.  */
5de29b
+#endif
5de29b
 	orc	r9,r12,r5     /* Mask bits that are not part of the string.  */
5de29b
 	cmpb	r10,r9,r0     /* Check for null bytes in WORD1.  */
5de29b
 	cmpwi	cr7,r10,0     /* If r10 == 0, no null's have been found.  */
5de29b
@@ -49,9 +53,6 @@
5de29b
 	cmpb	r10,r12,r0
5de29b
 	cmpwi	cr7,r10,0
5de29b
 	bne	cr7,L(done)
5de29b
-	b	L(loop)	      /* We branch here (rather than falling through)
5de29b
-				 to skip the nops due to heavy alignment
5de29b
-				 of the loop below.  */
5de29b
 
5de29b
 	/* Main loop to look for the end of the string.  Since it's a
5de29b
 	   small loop (< 8 instructions), align it to 32-bytes.  */
5de29b
@@ -88,9 +89,15 @@
5de29b
 	   0xff in the same position as the null byte in the original
5de29b
 	   word from the string.  Use that to calculate the length.  */
5de29b
 L(done):
5de29b
-	cntlzw	r0,r10	      /* Count leading zeroes before the match.  */
5de29b
+#ifdef __LITTLE_ENDIAN__
5de29b
+	addi	r9, r10, -1   /* Form a mask from trailing zeros.  */
5de29b
+	andc	r9, r9, r10
5de29b
+	popcntw r0, r9	      /* Count the bits in the mask.  */
5de29b
+#else
5de29b
+	cntlzw	r0,r10	      /* Count leading zeros before the match.  */
5de29b
+#endif
5de29b
 	subf	r5,r3,r4
5de29b
-	srwi	r0,r0,3	      /* Convert leading zeroes to bytes.  */
5de29b
+	srwi	r0,r0,3	      /* Convert leading zeros to bytes.  */
5de29b
 	add	r3,r5,r0      /* Compute final length.  */
5de29b
 	blr
5de29b
 END (BP_SYM (strlen))
12745e
diff -urN glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/strlen.S glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/strlen.S
12745e
--- glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/strlen.S	2014-05-28 12:28:44.000000000 -0500
12745e
+++ glibc-2.17-c758a686/sysdeps/powerpc/powerpc32/strlen.S	2014-05-28 12:32:24.000000000 -0500
5de29b
@@ -31,7 +31,12 @@
5de29b
       1 is subtracted you get a value in the range 0x00-0x7f, none of which
5de29b
       have their high bit set. The expression here is
5de29b
       (x + 0xfefefeff) & ~(x | 0x7f7f7f7f), which gives 0x00000000 when
5de29b
-      there were no 0x00 bytes in the word.
5de29b
+      there were no 0x00 bytes in the word.  You get 0x80 in bytes that
5de29b
+      match, but possibly false 0x80 matches in the next more significant
5de29b
+      byte to a true match due to carries.  For little-endian this is
5de29b
+      of no consequence since the least significant match is the one
5de29b
+      we're interested in, but big-endian needs method 2 to find which
5de29b
+      byte matches.
5de29b
 
5de29b
    2) Given a word 'x', we can test to see _which_ byte was zero by
5de29b
       calculating ~(((x & 0x7f7f7f7f) + 0x7f7f7f7f) | x | 0x7f7f7f7f).
5de29b
@@ -74,7 +79,7 @@
5de29b
 
5de29b
 ENTRY (BP_SYM (strlen))
5de29b
 
5de29b
-#define rTMP1	r0
5de29b
+#define rTMP4	r0
5de29b
 #define rRTN	r3	/* incoming STR arg, outgoing result */
5de29b
 #define rSTR	r4	/* current string position */
5de29b
 #define rPADN	r5	/* number of padding bits we prepend to the
5de29b
@@ -84,9 +89,9 @@
5de29b
 #define rWORD1	r8	/* current string word */
5de29b
 #define rWORD2	r9	/* next string word */
5de29b
 #define rMASK	r9	/* mask for first string word */
5de29b
-#define rTMP2	r10
5de29b
-#define rTMP3	r11
5de29b
-#define rTMP4	r12
5de29b
+#define rTMP1	r10
5de29b
+#define rTMP2	r11
5de29b
+#define rTMP3	r12
5de29b
 
5de29b
 	CHECK_BOUNDS_LOW (rRTN, rTMP1, rTMP2)
5de29b
 
5de29b
@@ -96,15 +101,20 @@
5de29b
 	lwz	rWORD1, 0(rSTR)
5de29b
 	li	rMASK, -1
5de29b
 	addi	r7F7F, r7F7F, 0x7f7f
5de29b
-/* That's the setup done, now do the first pair of words.
5de29b
-   We make an exception and use method (2) on the first two words, to reduce
5de29b
-   overhead.  */
5de29b
+/* We use method (2) on the first two words, because rFEFE isn't
5de29b
+   required which reduces setup overhead.  Also gives a faster return
5de29b
+   for small strings on big-endian due to needing to recalculate with
5de29b
+   method (2) anyway.  */
5de29b
+#ifdef __LITTLE_ENDIAN__
5de29b
+	slw	rMASK, rMASK, rPADN
5de29b
+#else
5de29b
 	srw	rMASK, rMASK, rPADN
5de29b
+#endif
5de29b
 	and	rTMP1, r7F7F, rWORD1
5de29b
 	or	rTMP2, r7F7F, rWORD1
5de29b
 	add	rTMP1, rTMP1, r7F7F
5de29b
-	nor	rTMP1, rTMP2, rTMP1
5de29b
-	and.	rWORD1, rTMP1, rMASK
5de29b
+	nor	rTMP3, rTMP2, rTMP1
5de29b
+	and.	rTMP3, rTMP3, rMASK
5de29b
 	mtcrf	0x01, rRTN
5de29b
 	bne	L(done0)
5de29b
 	lis	rFEFE, -0x101
5de29b
@@ -113,11 +123,12 @@
5de29b
 	bt	29, L(loop)
5de29b
 
5de29b
 /* Handle second word of pair.  */
5de29b
+/* Perhaps use method (1) here for little-endian, saving one instruction?  */
5de29b
 	lwzu	rWORD1, 4(rSTR)
5de29b
 	and	rTMP1, r7F7F, rWORD1
5de29b
 	or	rTMP2, r7F7F, rWORD1
5de29b
 	add	rTMP1, rTMP1, r7F7F
5de29b
-	nor.	rWORD1, rTMP2, rTMP1
5de29b
+	nor.	rTMP3, rTMP2, rTMP1
5de29b
 	bne	L(done0)
5de29b
 
5de29b
 /* The loop.  */
5de29b
@@ -131,29 +142,53 @@
5de29b
 	add	rTMP3, rFEFE, rWORD2
5de29b
 	nor	rTMP4, r7F7F, rWORD2
5de29b
 	bne	L(done1)
5de29b
-	and.	rTMP1, rTMP3, rTMP4
5de29b
+	and.	rTMP3, rTMP3, rTMP4
5de29b
 	beq	L(loop)
5de29b
 
5de29b
+#ifndef __LITTLE_ENDIAN__
5de29b
 	and	rTMP1, r7F7F, rWORD2
5de29b
 	add	rTMP1, rTMP1, r7F7F
5de29b
-	andc	rWORD1, rTMP4, rTMP1
5de29b
+	andc	rTMP3, rTMP4, rTMP1
5de29b
 	b	L(done0)
5de29b
 
5de29b
 L(done1):
5de29b
 	and	rTMP1, r7F7F, rWORD1
5de29b
 	subi	rSTR, rSTR, 4
5de29b
 	add	rTMP1, rTMP1, r7F7F
5de29b
-	andc	rWORD1, rTMP2, rTMP1
5de29b
+	andc	rTMP3, rTMP2, rTMP1
5de29b
 
5de29b
 /* When we get to here, rSTR points to the first word in the string that
5de29b
-   contains a zero byte, and the most significant set bit in rWORD1 is in that
5de29b
-   byte.  */
5de29b
+   contains a zero byte, and rTMP3 has 0x80 for bytes that are zero,
5de29b
+   and 0x00 otherwise.  */
5de29b
 L(done0):
5de29b
-	cntlzw	rTMP3, rWORD1
5de29b
+	cntlzw	rTMP3, rTMP3
5de29b
 	subf	rTMP1, rRTN, rSTR
5de29b
 	srwi	rTMP3, rTMP3, 3
5de29b
 	add	rRTN, rTMP1, rTMP3
5de29b
 	/* GKM FIXME: check high bound.  */
5de29b
 	blr
5de29b
+#else
5de29b
+
5de29b
+L(done0):
5de29b
+	addi	rTMP1, rTMP3, -1	/* Form a mask from trailing zeros.  */
5de29b
+	andc	rTMP1, rTMP1, rTMP3
5de29b
+	cntlzw	rTMP1, rTMP1		/* Count bits not in the mask.  */
5de29b
+	subf	rTMP3, rRTN, rSTR
5de29b
+	subfic	rTMP1, rTMP1, 32-7
5de29b
+	srwi	rTMP1, rTMP1, 3
5de29b
+	add	rRTN, rTMP1, rTMP3
5de29b
+	blr
5de29b
+
5de29b
+L(done1):
5de29b
+	addi	rTMP3, rTMP1, -1
5de29b
+	andc	rTMP3, rTMP3, rTMP1
5de29b
+	cntlzw	rTMP3, rTMP3
5de29b
+	subf	rTMP1, rRTN, rSTR
5de29b
+	subfic	rTMP3, rTMP3, 32-7-32
5de29b
+	srawi	rTMP3, rTMP3, 3
5de29b
+	add	rRTN, rTMP1, rTMP3
5de29b
+	blr
5de29b
+#endif
5de29b
+
5de29b
 END (BP_SYM (strlen))
5de29b
 libc_hidden_builtin_def (strlen)
12745e
diff -urN glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/power7/strlen.S glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/power7/strlen.S
12745e
--- glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/power7/strlen.S	2014-05-28 12:28:44.000000000 -0500
12745e
+++ glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/power7/strlen.S	2014-05-28 12:28:45.000000000 -0500
5de29b
@@ -32,7 +32,11 @@
5de29b
 				 with cmpb.  */
5de29b
 	li	r5,-1	      /* MASK = 0xffffffffffffffff.  */
5de29b
 	ld	r12,0(r4)     /* Load doubleword from memory.  */
5de29b
+#ifdef __LITTLE_ENDIAN__
5de29b
+	sld	r5,r5,r6
5de29b
+#else
5de29b
 	srd	r5,r5,r6      /* MASK = MASK >> padding.  */
5de29b
+#endif
5de29b
 	orc	r9,r12,r5     /* Mask bits that are not part of the string.  */
5de29b
 	cmpb	r10,r9,r0     /* Check for null bytes in DWORD1.  */
5de29b
 	cmpdi	cr7,r10,0     /* If r10 == 0, no null's have been found.  */
5de29b
@@ -50,9 +54,6 @@
5de29b
 	cmpb	r10,r12,r0
5de29b
 	cmpdi	cr7,r10,0
5de29b
 	bne	cr7,L(done)
5de29b
-	b	L(loop)	      /* We branch here (rather than falling through)
5de29b
-				 to skip the nops due to heavy alignment
5de29b
-				 of the loop below.  */
5de29b
 
5de29b
 	/* Main loop to look for the end of the string.  Since it's a
5de29b
 	   small loop (< 8 instructions), align it to 32-bytes.  */
5de29b
@@ -89,9 +90,15 @@
5de29b
 	   0xff in the same position as the null byte in the original
5de29b
 	   doubleword from the string.  Use that to calculate the length.  */
5de29b
 L(done):
5de29b
-	cntlzd	r0,r10	      /* Count leading zeroes before the match.  */
5de29b
+#ifdef __LITTLE_ENDIAN__
5de29b
+	addi	r9, r10, -1   /* Form a mask from trailing zeros.  */
5de29b
+	andc	r9, r9, r10
5de29b
+	popcntd r0, r9	      /* Count the bits in the mask.  */
5de29b
+#else
5de29b
+	cntlzd	r0,r10	      /* Count leading zeros before the match.  */
5de29b
+#endif
5de29b
 	subf	r5,r3,r4
5de29b
-	srdi	r0,r0,3	      /* Convert leading zeroes to bytes.  */
5de29b
+	srdi	r0,r0,3	      /* Convert leading/trailing zeros to bytes.  */
5de29b
 	add	r3,r5,r0      /* Compute final length.  */
5de29b
 	blr
5de29b
 END (BP_SYM (strlen))
12745e
diff -urN glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/strlen.S glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/strlen.S
12745e
--- glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/strlen.S	2014-05-28 12:28:44.000000000 -0500
12745e
+++ glibc-2.17-c758a686/sysdeps/powerpc/powerpc64/strlen.S	2014-05-28 12:38:17.000000000 -0500
5de29b
@@ -31,7 +31,12 @@
5de29b
       1 is subtracted you get a value in the range 0x00-0x7f, none of which
5de29b
       have their high bit set. The expression here is
5de29b
       (x + 0xfefefeff) & ~(x | 0x7f7f7f7f), which gives 0x00000000 when
5de29b
-      there were no 0x00 bytes in the word.
5de29b
+      there were no 0x00 bytes in the word.  You get 0x80 in bytes that
5de29b
+      match, but possibly false 0x80 matches in the next more significant
5de29b
+      byte to a true match due to carries.  For little-endian this is
5de29b
+      of no consequence since the least significant match is the one
5de29b
+      we're interested in, but big-endian needs method 2 to find which
5de29b
+      byte matches.
5de29b
 
5de29b
    2) Given a word 'x', we can test to see _which_ byte was zero by
5de29b
       calculating ~(((x & 0x7f7f7f7f) + 0x7f7f7f7f) | x | 0x7f7f7f7f).
5de29b
@@ -64,7 +69,7 @@
5de29b
    Answer:
5de29b
    1) Added a Data Cache Block Touch early to prefetch the first 128 
5de29b
    byte cache line. Adding dcbt instructions to the loop would not be 
5de29b
-   effective since most strings will be shorter than the cache line.*/
5de29b
+   effective since most strings will be shorter than the cache line.  */
5de29b
 
5de29b
 /* Some notes on register usage: Under the SVR4 ABI, we can use registers
5de29b
    0 and 3 through 12 (so long as we don't call any procedures) without
5de29b
@@ -80,7 +85,7 @@
5de29b
 ENTRY (BP_SYM (strlen))
5de29b
 	CALL_MCOUNT 1
5de29b
 
5de29b
-#define rTMP1	r0
5de29b
+#define rTMP4	r0
5de29b
 #define rRTN	r3	/* incoming STR arg, outgoing result */
5de29b
 #define rSTR	r4	/* current string position */
5de29b
 #define rPADN	r5	/* number of padding bits we prepend to the
5de29b
@@ -90,9 +95,9 @@
5de29b
 #define rWORD1	r8	/* current string doubleword */
5de29b
 #define rWORD2	r9	/* next string doubleword */
5de29b
 #define rMASK	r9	/* mask for first string doubleword */
5de29b
-#define rTMP2	r10
5de29b
-#define rTMP3	r11
5de29b
-#define rTMP4	r12
5de29b
+#define rTMP1	r10
5de29b
+#define rTMP2	r11
5de29b
+#define rTMP3	r12
5de29b
 
5de29b
 /* Note:  The Bounded pointer support in this code is broken.  This code
5de29b
    was inherited from PPC32 and that support was never completed.
5de29b
@@ -109,30 +114,36 @@
5de29b
 	addi	r7F7F, r7F7F, 0x7f7f
5de29b
 	li	rMASK, -1
5de29b
 	insrdi	r7F7F, r7F7F, 32, 0
5de29b
-/* That's the setup done, now do the first pair of doublewords.
5de29b
-   We make an exception and use method (2) on the first two doublewords, 
5de29b
-   to reduce overhead.  */
5de29b
-	srd	rMASK, rMASK, rPADN
5de29b
+/* We use method (2) on the first two doublewords, because rFEFE isn't
5de29b
+   required which reduces setup overhead.  Also gives a faster return
5de29b
+   for small strings on big-endian due to needing to recalculate with
5de29b
+   method (2) anyway.  */
5de29b
+#ifdef __LITTLE_ENDIAN__
5de29b
+	sld	rMASK, rMASK, rPADN
5de29b
+#else
5de29b
+ 	srd	rMASK, rMASK, rPADN
5de29b
+#endif
5de29b
 	and	rTMP1, r7F7F, rWORD1
5de29b
 	or	rTMP2, r7F7F, rWORD1
5de29b
 	lis	rFEFE, -0x101
5de29b
 	add	rTMP1, rTMP1, r7F7F
5de29b
 	addi	rFEFE, rFEFE, -0x101
5de29b
-	nor	rTMP1, rTMP2, rTMP1
5de29b
-	and.	rWORD1, rTMP1, rMASK
5de29b
+	nor	rTMP3, rTMP2, rTMP1
5de29b
+	and.	rTMP3, rTMP3, rMASK
5de29b
 	mtcrf	0x01, rRTN
5de29b
 	bne	L(done0)
5de29b
-	sldi  rTMP1, rFEFE, 32
5de29b
-	add  rFEFE, rFEFE, rTMP1
5de29b
+	sldi	rTMP1, rFEFE, 32
5de29b
+	add	rFEFE, rFEFE, rTMP1
5de29b
 /* Are we now aligned to a doubleword boundary?  */
5de29b
 	bt	28, L(loop)
5de29b
 
5de29b
 /* Handle second doubleword of pair.  */
5de29b
+/* Perhaps use method (1) here for little-endian, saving one instruction?  */
5de29b
 	ldu	rWORD1, 8(rSTR)
5de29b
 	and	rTMP1, r7F7F, rWORD1
5de29b
 	or	rTMP2, r7F7F, rWORD1
5de29b
 	add	rTMP1, rTMP1, r7F7F
5de29b
-	nor.	rWORD1, rTMP2, rTMP1
5de29b
+	nor.	rTMP3, rTMP2, rTMP1
5de29b
 	bne	L(done0)
5de29b
 
5de29b
 /* The loop.  */
5de29b
@@ -146,29 +157,53 @@
5de29b
 	add	rTMP3, rFEFE, rWORD2
5de29b
 	nor	rTMP4, r7F7F, rWORD2
5de29b
 	bne	L(done1)
5de29b
-	and.	rTMP1, rTMP3, rTMP4
5de29b
+	and.	rTMP3, rTMP3, rTMP4
5de29b
 	beq	L(loop)
5de29b
 
5de29b
+#ifndef __LITTLE_ENDIAN__
5de29b
 	and	rTMP1, r7F7F, rWORD2
5de29b
 	add	rTMP1, rTMP1, r7F7F
5de29b
-	andc	rWORD1, rTMP4, rTMP1
5de29b
+	andc	rTMP3, rTMP4, rTMP1
5de29b
 	b	L(done0)
5de29b
 
5de29b
 L(done1):
5de29b
 	and	rTMP1, r7F7F, rWORD1
5de29b
 	subi	rSTR, rSTR, 8
5de29b
 	add	rTMP1, rTMP1, r7F7F
5de29b
-	andc	rWORD1, rTMP2, rTMP1
5de29b
+	andc	rTMP3, rTMP2, rTMP1
5de29b
 
5de29b
 /* When we get to here, rSTR points to the first doubleword in the string that
5de29b
-   contains a zero byte, and the most significant set bit in rWORD1 is in that
5de29b
-   byte.  */
5de29b
+   contains a zero byte, and rTMP3 has 0x80 for bytes that are zero, and 0x00
5de29b
+   otherwise.  */
5de29b
 L(done0):
5de29b
-	cntlzd	rTMP3, rWORD1
5de29b
+	cntlzd	rTMP3, rTMP3
5de29b
 	subf	rTMP1, rRTN, rSTR
5de29b
 	srdi	rTMP3, rTMP3, 3
5de29b
 	add	rRTN, rTMP1, rTMP3
5de29b
 	/* GKM FIXME: check high bound.  */
5de29b
 	blr
5de29b
+#else
5de29b
+
5de29b
+L(done0):
5de29b
+	addi	rTMP1, rTMP3, -1	/* Form a mask from trailing zeros.  */
5de29b
+	andc	rTMP1, rTMP1, rTMP3
5de29b
+	cntlzd	rTMP1, rTMP1		/* Count bits not in the mask.  */
5de29b
+	subf	rTMP3, rRTN, rSTR
5de29b
+	subfic	rTMP1, rTMP1, 64-7
5de29b
+	srdi	rTMP1, rTMP1, 3
5de29b
+	add	rRTN, rTMP1, rTMP3
5de29b
+	blr
5de29b
+
5de29b
+L(done1):
5de29b
+	addi	rTMP3, rTMP1, -1
5de29b
+	andc	rTMP3, rTMP3, rTMP1
5de29b
+	cntlzd	rTMP3, rTMP3
5de29b
+	subf	rTMP1, rRTN, rSTR
5de29b
+	subfic	rTMP3, rTMP3, 64-7-64
5de29b
+	sradi	rTMP3, rTMP3, 3
5de29b
+	add	rRTN, rTMP1, rTMP3
5de29b
+	blr
5de29b
+#endif
5de29b
+
5de29b
 END (BP_SYM (strlen))
5de29b
 libc_hidden_builtin_def (strlen)