web-dev-qa-db-ja.com

関数呼び出しが返されないことをgccに伝えます

GCCの下で_C99_を使用しています。

変更できないヘッダーにstaticinlineと宣言された関数があります。

関数は決して戻りませんが、__attribute__((noreturn))とマークされていません。

コンパイラに戻らないように指示する方法で関数を呼び出すにはどうすればよいですか?

自分のnoreturn関数から呼び出していますが、「noreturn関数が戻る」という警告を部分的に抑制したいのですが、オプティマイザーなども支援したいと思っています。

属性に宣言を含めようとしましたが、宣言が繰り返されると警告が表示されます。

関数ポインタを作成してそれに属性を適用しようとしましたが、関数属性は指定された関数には適用できないと表示されます。

24
Tom

いくつかの解決策:

__attribute__を使用して関数を再宣言する

ヘッダーに__attribute__((noreturn))を追加して、その関数を変更してみてください。

このばかげたテストが示すように、いくつかの関数を新しい属性で再宣言できます(fopenに属性を追加します):

 #include <stdio.h>

 extern FILE *fopen (const char *__restrict __filename,
            const char *__restrict __modes)
   __attribute__ ((warning ("fopen is used")));

 void
 show_map_without_care (void)
 {
   FILE *f = fopen ("/proc/self/maps", "r");
   do
     {
       char lin[64];
       fgets (lin, sizeof (lin), f);
       fputs (lin, stdout);
     }
   while (!feof (f));
   fclose (f);
 }

マクロでオーバーライドする

最後に、次のようなマクロを定義できます

#define func(A) {func(A); __builtin_unreachable();}

(これは、マクロ内でマクロ名がマクロ展開されないという事実を使用します)。

戻ってこないfuncが、戻ってくると宣言している場合。 int使用します ステートメント式 like

#define func(A) ({func(A); __builtin_unreachable(); (int)0; })

上記のようなマクロベースのソリューションは、常に機能するとは限りません。 funcが関数ポインタとして渡された場合、または単に誰かが(func)(1)をコーディングした場合、これは合法ですが醜いです。


noreturn属性を使用して静的インラインを再宣言する

そして次の例:

 // file ex.c
 // declare exit without any standard header
 void exit (int);

 // define myexit as a static inline
 static inline void
 myexit (int c)
 {
   exit (c);
 }

 // redeclare it as notreturn
 static inline void myexit (int c) __attribute__ ((noreturn));

 int
 foo (int *p)
 {
   if (!p)
     myexit (1);
   if (p)
     return *p + 2;
   return 0;
 }

gCC 4.9(Debian/Sid/x86-64から)をgcc -S -fverbose-asm -O2 ex.cとしてコンパイルすると、期待される最適化を含むアセンブリファイルが得られます。

         .type   foo, @function
 foo:
 .LFB1:
    .cfi_startproc
    testq   %rdi, %rdi      # p
    je      .L5     #,
    movl    (%rdi), %eax    # *p_2(D), *p_2(D)
    addl    $2, %eax        #, D.1768
    ret
.L5:
    pushq   %rax    #
    .cfi_def_cfa_offset 16
    movb    $1, %dil        #,
    call    exit    #
    .cfi_endproc
 .LFE1:
    .size   foo, .-foo

#pragma GCC Diagnostics で遊んで、警告を選択的に無効にすることができます。


カスタマイズ [〜#〜] gcc [〜#〜] with [〜#〜] melt [〜#〜]

最後に、 [〜#〜] melt [〜#〜] プラグインを使用して、最近のgccをカスタマイズし、単純な拡張機能をコーディングすることができます([ 〜#〜] melt [〜#〜]ドメイン固有言語)目的の関数を実行するときに属性noreturnを追加します。 register_finish_decl_firstと関数名の一致を使用した、おそらく数十のMELT行です。

私は[〜#〜] melt [〜#〜](フリーソフトウェアGPLv3 +)の主な作者なので、あなたが尋ねれば、おそらくそれをコーディングすることさえできます。ここまたはできれば[email protected];戻らない関数の具体的な名前を付けてください。

おそらく、MELTコードは次のようになります。

  ;;file your_melt_mode.melt
  (module_is_gpl_compatible "GPLv3+")
  (defun my_finish_decl (decl)
     (let ( (tdecl (unbox :tree decl))
       )
     (match tdecl
        (?(tree_function_decl_named
            ?(tree_identifier ?(cstring_same "your_function_name")))
          ;;; code to add the noreturn attribute
          ;;; ....
        ))))
  (register_finish_decl_first my_finish_decl)

実際のMELTコードは少し複雑です。そこでyour_adding_attr_modeを定義したいとします。詳細はお問い合わせください。

必要に応じてMELT拡張機能をyour_melt_mode.meltでコーディングしたら(そして、MELTチュートリアルでそのMELT拡張機能をyour_melt_mode.quicklybuilt.sodocumented としてコンパイルすると)、コードを

  gcc -fplugin=melt \
      -fplugin-arg-melt-extra=your_melt_mode.quicklybuilt \
      -fplugin-arg-melt-mode=your_adding_attr_mode \
      -O2 -I/your/include -c yourfile.c

つまり、CFLAGSMakefileにいくつかの-fplugin-*フラグを追加するだけです。

ところで、私はMELTモニターでコーディングしているだけです(github: https://github.com/bstarynk/melt-monitor ...、ファイルmeltmom-process.melt非常によく似ています。

MELT拡張機能を使用すると、内部GCC AST(GCCTree)が変更されるため、追加の警告は表示されません。宣言された関数のオンザフライ!

MELTを使用してGCCをカスタマイズすることは、GCCの内部ASTを変更するため、おそらく最も防弾のソリューションです。もちろん、これはおそらく最もコストのかかるソリューションです(GCC固有であり、GCCが進化しているとき、たとえば次のバージョンのGCCを使用するときなど、小さな変更が必要になる場合があります)が、私が示しているように、あなたの場合。

PS。 2019年、GCCMELTは放棄されたプロジェクトです。 GCCをカスタマイズする場合(GCCの最近のバージョン(GCC 7、8、9など)の場合)、C++で独自の GCCプラグイン を作成する必要があります。