web-dev-qa-db-fra.com

Où est l'implémentation de strlen () dans GCC?

Quelqu'un peut-il m'indiquer la définition de strlen() dans GCC? Cela fait environ une demi-heure que je travaille dans la version 4.4.2 de Grepping (alors que Google googe comme un fou) et je n'arrive pas à trouver où strlen() est réellement implémenté.

18
Chris Tonkinson

Vous devriez regarder dans glibc, pas dans GCC - cela semble être défini dans strlen.c - voici un lien vers strlen.c pour la version 2.7 de glibc ... Et voici un lien vers le référentiel glibc SVN en ligne pour strlen.c .

La raison pour laquelle vous devriez regarder glibc et non gcc est:

La bibliothèque GNU C est utilisée en tant que la bibliothèque C dans le système GNU et dans la plupart des systèmes dotés du noyau Linux.

30
Mark Rushakoff

Je me rends compte que cette question a 4 ans, mais gcc inclura souvent sa own copie de strlen si vous ne le faites pas #include <string.h> et aucune des réponses (y compris la réponse acceptée) ne rend compte de cela. Si vous oubliez, vous recevrez un avertissement:

file_name:line_number: warning: incompatible implicit declaration of built-in function 'strlen'

et gcc ajoutera sa copie qui, sur x86, correspond à la variante repnz scasb asm, à moins que vous ne passiez -Werror ou -fno-builtin. Les fichiers liés à cela sont dans gcc/config/<platform>/<platform>.{c,md}

Il est également contrôlé par gcc/builtins.c. Si vous vous demandez si et comment une strlen () a été optimisée pour une constante, reportez-vous à la fonction définie par tree c_strlen(tree src, int only_value) dans ce fichier. Il contrôle également la manière dont strlen (entre autres) est développé et replié (sur la base de la configuration/plateforme mentionnée précédemment)

10
technosaurus

Voici la bsd implementation

size_t
strlen(const char *str)
{
        const char *s;

        for (s = str; *s; ++s)
                ;
        return (s - str);
}
7
hyperlogic

Bien que l’affiche originale n’ait peut-être pas su cela ou l’ait cherchée, gcc inclut en interne un certain nombre de fonctions c dites "intégrées" qu’elle définit elle-même, y compris certaines des fonctions mem * () et (selon le version gcc) strlen. Dans de tels cas, la version de la bibliothèque n’est essentiellement jamais utilisée, et pointer la personne vers la version dans la glibc n’est pas à proprement parler correcte. (C’est pour des raisons de performances - en plus de l’amélioration apportée par inline, gcc "connaît" certaines choses à propos des fonctions quand elle les fournit, comme par exemple que strlen est une fonction pure et qu’elle peut ainsi optimiser plusieurs appels ou, dans le cas des fonctions mem * (), aucun aliasing n’a lieu.)

Pour plus d'informations à ce sujet, voir http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

3
Perry

Est-ce ce que vous recherchez? strlen () source . Voir le répertoire git pour plus d’informations. La page des ressources de glibc contient des liens vers les référentiels git si vous souhaitez les récupérer plutôt que de regarder la vue Web.

3
faran

Recherche de code Google est un bon point de départ pour de telles questions. Ils indiquent généralement diverses sources et implémentations d’une fonction.

Dans votre cas particulier: GoogleCodeSearch (strlen)

La recherche de code Google a été complètement fermée en mars 2013

3
cschol

défini dans glibc/string/strlen.c

#include <string.h>
#include <stdlib.h>

#undef strlen

#ifndef STRLEN
# define STRLEN strlen
#endif

/* Return the length of the null-terminated string STR.  Scan for
   the null terminator quickly by testing four bytes at a time.  */
size_t
STRLEN (const char *str)
{
  const char *char_ptr;
  const unsigned long int *longword_ptr;
  unsigned long int longword, himagic, lomagic;

  /* Handle the first few characters by reading one character at a time.
     Do this until CHAR_PTR is aligned on a longword boundary.  */
  for (char_ptr = str; ((unsigned long int) char_ptr
            & (sizeof (longword) - 1)) != 0;
       ++char_ptr)
    if (*char_ptr == '\0')
      return char_ptr - str;

  /* All these elucidatory comments refer to 4-byte longwords,
     but the theory applies equally well to 8-byte longwords.  */

  longword_ptr = (unsigned long int *) char_ptr;

  /* Bits 31, 24, 16, and 8 of this number are zero.  Call these bits
     the "holes."  Note that there is a hole just to the left of
     each byte, with an extra at the end:

     bits:  01111110 11111110 11111110 11111111
     bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD

     The 1-bits make sure that carries propagate to the next 0-bit.
     The 0-bits provide holes for carries to fall into.  */
  himagic = 0x80808080L;
  lomagic = 0x01010101L;
  if (sizeof (longword) > 4)
    {
      /* 64-bit version of the magic.  */
      /* Do the shift in two steps to avoid a warning if long has 32 bits.  */
      himagic = ((himagic << 16) << 16) | himagic;
      lomagic = ((lomagic << 16) << 16) | lomagic;
    }
  if (sizeof (longword) > 8)
    abort ();

  /* Instead of the traditional loop which tests each character,
     we will test a longword at a time.  The tricky part is testing
     if *any of the four* bytes in the longword in question are zero.  */
  for (;;)
    {
      longword = *longword_ptr++;

      if (((longword - lomagic) & ~longword & himagic) != 0)
    {
      /* Which of the bytes was the zero?  If none of them were, it was
         a misfire; continue the search.  */

      const char *cp = (const char *) (longword_ptr - 1);

      if (cp[0] == 0)
        return cp - str;
      if (cp[1] == 0)
        return cp - str + 1;
      if (cp[2] == 0)
        return cp - str + 2;
      if (cp[3] == 0)
        return cp - str + 3;
      if (sizeof (longword) > 4)
        {
          if (cp[4] == 0)
        return cp - str + 4;
          if (cp[5] == 0)
        return cp - str + 5;
          if (cp[6] == 0)
        return cp - str + 6;
          if (cp[7] == 0)
        return cp - str + 7;
        }
    }
    }
}
libc_hidden_builtin_def (strlen)
3
X Stylish

Je me rends compte que c’est une vieille question, vous pouvez trouver les sources du noyau Linux sur github ici , et l’implémentation 32 bits de strlen () pourrait être trouvée dans strlen_32.c sur github. Le fichier mentionné a cette implémentation.

#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);
1
A.B.

La glibc 2.26 dispose de plusieurs implémentations Assembly optimisées à la main de strlen

À partir de glibc-2.26, un rapide:

git ls-files | grep strlen.S

dans l'arborescence de la glibc montre une douzaine d'implémentations d'assemblage optimisées manuellement pour tous les principaux arcs et variations.

En particulier, x86_64 à lui seul comporte 3 variantes:

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

Un moyen rapide et correct de déterminer lequel est utilisé consiste à déboguer par étapes un programme de test:

#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;
}

compilé avec:

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

Dès le départ:

disass main

contient:

callq  0x555555554590 <strlen@plt>

alors la version de libc est appelée.

Après quelques étapes du niveau d'instruction si, GDB atteint:

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

ce qui me dit que strlen-avx2.S a été utilisé.

Ensuite, je confirme avec:

disass __strlen_avx2

et comparez le démontage avec la source glibc.

Il n’est pas surprenant que la version AVX2 ait été utilisée, car j’ai un processeur i7-7820HQ avec une date de lancement au premier trimestre 2017 et le support AVX2, et AVX2 est l’implémentation la plus avancée de Assembly, avec une date de lancement T2 2013, alors que SSE2 est bien plus ancien qu’en 2004.

C’est d’où vient en grande partie la dureté de glibc: il a beaucoup de code Assembly assemblé à la main optimisé pour Arch.

Testé sous Ubuntu 17.10, gcc 7.2.0, glibc 2.26.

-O3

TODO: avec -O3, gcc n'utilise pas la variable strlen de glibc, il génère simplement un assemblage en ligne, mentionné à l'adresse: https://stackoverflow.com/a/19885891/895245

Est-ce parce qu'il peut encore mieux optimiser? Mais sa sortie ne contient pas d'instructions AVX2, donc j'estime que ce n'est pas le cas.

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

Déficiences de l'optimiseur de GCC

la glibc a des versions inline assembler de diverses fonctions string; GCC en a, mais pas nécessairement les mêmes, sur les mêmes architectures. Des entrées optab supplémentaires, telles que celles de ffs et strlen, pourraient être fournies pour plusieurs fonctions supplémentaires, notamment memset, strchr, strcpy et strrchr.

Mes tests simples montrent que la version -O3 est en réalité plus rapide. GCC a donc fait le bon choix.

Interrogé à: https://www.quora.com/unanswered/How-does-GCC-know-that-its-builtin-implementation-of-strlen-is-faster-than-glibcs-when-using-optimization -niveau-O3

Vous pouvez utiliser ce code, le plus simple sera le mieux!

size_t Strlen ( const char * _str )
{
    size_t i = 0;
    while(_str[i++]);
    return i;
}
0
Alisher Aliev