Where is the implementation of strlen() in GCC?

后端 未结 10 1072
梦毁少年i
梦毁少年i 2021-02-04 01:21

Can anyone point me to the definition of strlen() in GCC? I\'ve been grepping release 4.4.2 for about a half hour now (while Googling like crazy) and I can\'t seem

相关标签:
10条回答
  • 2021-02-04 01:58

    Although the original poster may not have known this or been looking for this, gcc internally inlines a number of so-called "builtin" c functions that it defines on its own, including some of the mem*() functions and (depending on the gcc version) strlen. In such cases, the library version is essentially never used, and pointing the person at the version in glibc is not strictly speaking correct. (It does this for performance reasons -- in addition to the improvement that inlining itself produces, gcc "knows" certain things about the functions when it provides them, such as, for example, that strlen is a pure function and that it can thus optimize away multiple calls, or in the case of the mem*() functions that no aliasing is taking place.)

    For more information on this, see http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

    0 讨论(0)
  • 2021-02-04 01:59

    glibc 2.26 has several hand optimized assembly implementations of strlen

    As of glibc-2.26, a quick:

    git ls-files | grep strlen.S
    

    in the glibc tree shows a dozen of assembly hand-optimized implementations for all major archs and variations.

    In particular, x86_64 alone has 3 variations:

    sysdeps/x86_64/multiarch/strlen-avx2.S
    sysdeps/x86_64/multiarch/strlen-sse2.S
    sysdeps/x86_64/strlen.S
    

    A quick and dirty way to determine which one is used, is to step debug a test program:

    #include <assert.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    int main(void) {
        size_t size = 0x80000000, i, result;
        char *s = malloc(size);
        for (i = 0; i < size; ++i)
            s[i] = 'a';
        s[size - 1] = '\0';
        result = strlen(s);
        assert(result == size - 1);
        return EXIT_SUCCESS;
    }
    

    compiled with:

    gcc -ggdb3 -std=c99 -O0 a.c
    

    Off the bat:

    disass main
    

    contains:

    callq  0x555555554590 <strlen@plt>
    

    so the libc version is being called.

    After a few si instruction level steps into that, GDB reaches:

    __strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:52                                         
    52      ../sysdeps/x86_64/multiarch/strlen-avx2.S: No such file or directory.
    

    which tells me that strlen-avx2.S was used.

    Then, I further confirm with:

    disass __strlen_avx2
    

    and compare the disassembly with the glibc source.

    It is not surprising that the AVX2 version was used, since I have an i7-7820HQ CPU with launch date Q1 2017 and AVX2 support, and AVX2 is the most advanced of the assembly implementations, with launch date Q2 2013, while SSE2 is much more ancient from 2004.

    This is where a great part of the hardcoreness of glibc comes from: it has a lot of arch optimized hand written assembly code.

    Tested in Ubuntu 17.10, gcc 7.2.0, glibc 2.26.

    -O3

    TODO: with -O3, gcc does not use glibc's strlen, it just generates inline assembly, which is mentioned at: https://stackoverflow.com/a/19885891/895245

    Is it because it can optimize even better? But its output does not contain AVX2 instructions, so I feel that this is not the case.

    https://www.gnu.org/software/gcc/projects/optimize.html mentions:

    Deficiencies of GCC's optimizer

    glibc has inline assembler versions of various string functions; GCC has some, but not necessarily the same ones on the same architectures. Additional optab entries, like the ones for ffs and strlen, could be provided for several more functions including memset, strchr, strcpy and strrchr.

    My simple tests show that the -O3 version is actually faster, so GCC made the right choice.

    Asked at: https://www.quora.com/unanswered/How-does-GCC-know-that-its-builtin-implementation-of-strlen-is-faster-than-glibcs-when-using-optimization-level-O3

    0 讨论(0)
  • 2021-02-04 02:01

    Google Code Search is a good starting point for questions like that. They usually point to various different sources and implementations of a function.

    In your particular case: GoogleCodeSearch(strlen)

    Google Code Search was completely shut down on March 2013

    0 讨论(0)
  • 2021-02-04 02:04

    I realize that this is old question, you can find the linux kernel sources at github here, and the 32 bit implementation for strlen() could be found in strlen_32.c on github. The mentioned file has this implementation.

    #include <linux/types.h>
    #include <linux/string.h>
    #include <linux/module.h>
    
    size_t strlen(const char *s)
    {
        /* Get an aligned pointer. */
        const uintptr_t s_int = (uintptr_t) s;
        const uint32_t *p = (const uint32_t *)(s_int & -4);
    
        /* Read the first word, but force bytes before the string to be nonzero.
         * This expression works because we know shift counts are taken mod 32.
         */
        uint32_t v = *p | ((1 << (s_int << 3)) - 1);
    
        uint32_t bits;
        while ((bits = __insn_seqb(v, 0)) == 0)
            v = *++p;
    
        return ((const char *)p) + (__insn_ctz(bits) >> 3) - s;
    }
    EXPORT_SYMBOL(strlen);
    
    0 讨论(0)
提交回复
热议问题