web-dev-qa-db-ja.com

GCCのstrlen()の実装はどこにありますか?

誰かがGCCでのstrlen()の定義を私に指摘できますか?私はリリース4.4.2を約30分間(グーグルがおかしくなりながら)グレップしていて、strlen()が実際に実装されている場所を見つけることができないようです。

18
Chris Tonkinson

GCCではなくglibcを調べる必要があります-strlen.cで定義されているようです-glibcバージョン2.7の strlen.cへのリンクです ...そして、ここへのリンクです strlen.cのオンラインのglibc SVNリポジトリ

Gccではなく glibc を表示する必要がある理由は次のとおりです。

GNU CライブラリはtheCライブラリとしてGNUシステムで使用されますLinuxカーネルを備えたほとんどのシステム。

30
Mark Rushakoff

私はこの質問が4歳であることを理解していますが、_#include <string.h>_を使用せず、その回答(承認された回答を含む)のアカウントがない場合、gccにはstrlenのownコピーが含まれることがよくあります。忘れると、警告が表示されます。

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

また、gccは、-Werrorまたは-fno-builtinを渡さない限り、x86ではrepnz scasb asmバリアントであるコピーをインライン化します。これに関連するファイルは_gcc/config/<platform>/<platform>.{c,md}_にあります

また、gcc/builtins.cによって制御されます。 strlen()が定数に最適化されているかどうか、またどのように最適化されているかについては、このファイルでtree c_strlen(tree src, int only_value)として定義されている関数を参照してください。また、(とりわけ)strlenが(前述の構成/プラットフォームに基づいて)拡張および折りたたみされる方法も制御します。

12
technosaurus

これが bsd の実装です

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

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

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)
4
X Stylish

元の投稿者がこれを知らなかったり、探していなかったとしても、gccは内部で、いくつかのmem *()関数や( gccバージョン)strlen。このような場合、ライブラリのバージョンは基本的に使用されず、glibcでバージョンに人を向けることは厳密には正しくありません。 (これはパフォーマンス上の理由でこれを行います-インライン化自体がもたらす改善に加えて、gccは関数を提供するとき、たとえばstrlenが純粋な関数であり、したがって複数の呼び出し、またはmem *()関数の場合はエイリアシングが行われないように最適化します。)

この詳細については、 http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html を参照してください。

3
Perry

glibc 2.26には、strlenの手動で最適化されたアセンブリ実装がいくつかあります

glibc-2.26以降の簡単な説明:

git ls-files | grep strlen.S

glibcツリーのは、すべての主要なアーチとバリエーションについて、アセンブリを手動で最適化した12の実装を示しています。

特に、x86_64だけで3つのバリエーションがあります。

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

どちらが使用されているかをすばやく簡単に判断するには、テストプログラムをステップデバッグします。

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

コンパイル:

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

すぐに:

disass main

含む:

callq  0x555555554590 <strlen@plt>

したがって、libcバージョンが呼び出されています。

いくつかのsi命令レベルがそれに入ると、GDBは次のようになります。

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

strlen-avx2.S が使用されたことがわかります.

次に、さらに確認します。

disass __strlen_avx2

逆アセンブルとglibcソースを比較します。

発売日が2017年第1四半期で i7-7820HQ CPUがあり、AVX2がサポートされているため、AVX2バージョンが使用されたことは当然のことであり、 AVX2 はアセンブリの実装の中で最も進んでおり、発売日は2013年第2四半期ですが、 SSE2 は2004年よりはるかに古くなっています。

これがglibcのハードコア性の大きな部分の由来です。多くのArch最適化された手書きのアセンブリコードがあります。

Ubuntu 17.10、gcc 7.2.0、glibc 2.26でテスト済み。

-O3

TODO:-O3を使用すると、gccはglibcのstrlenを使用せず、インラインアセンブリを生成するだけです。これは、 で言及されています。https://stackoverflow.com/a/19885891/895245

それはさらに最適化できるからですか?しかし、その出力にはAVX2命令が含まれていないので、そうではないように感じます。

https://www.gnu.org/software/gcc/projects/optimize.html の言及:

GCCのオプティマイザの欠陥

glibcには、さまざまな文字列関数のインラインアセンブラバージョンがあります。 GCCには、同じアーキテクチャ上に同じものがありますが、必ずしも同じとは限りません。 ffsやstrlenのエントリのような追加のoptabエントリは、memset、strchr、strcpy、およびstrrchrを含むさらにいくつかの機能に提供できます。

私の簡単なテストでは、-O3バージョンの方が実際には高速であることを示しているため、GCCが正しい選択を行いました。

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

これはあなたが探しているものですか? strlen()ソース 。詳細は git repository を参照してください。 glibc resources page には、Webビューを表示するのではなく、それらを取得したい場合にgitリポジトリへのリンクがあります。

3
faran

Google Code Search は、そのような質問の良い出発点です。それらは通常、関数のさまざまなソースと実装を指します。

あなたの特定のケースでは: GoogleCodeSearch(strlen)

Google Code Searchは2013年3月に完全に閉鎖されました

3
cschol

これは古い質問だと思います。Linuxカーネルソースはgithub here にあり、strlen()の32ビット実装は strlen_32.c onにあります。 github。上記のファイルにはこの実装があります。

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

あなたはこのコードを使うことができます、よりシンプルであるほど良いです!

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