setjmpの結果を代入してはいけない
 okuoku
okuokuWasmLinuxで使っちゃってるし実際gccやclangでは正常に動作するんだけど一応...
 okuoku
okuokusetjmpを書いて良い場所
Cの規格やPOSIXでは setjmp を書ける場所が厳密に決まっている:
An application shall ensure that an invocation of setjmp() appears in one of the following contexts only:
- The entire controlling expression of a selection or iteration statement
- One operand of a relational or equality operator with the other operand an integral constant expression, with the resulting expression being the entire controlling expression of a selection or iteration statement
- The operand of a unary '!' operator with the resulting expression being the entire controlling expression of a selection or iteration
- The entire expression of an expression statement (possibly cast to void)
要は分岐にしか書けない。例えば、
if(i = setjmp(jb)){ ... }
のように i に代入するのは違反になる。
例えば https://www.jpcert.or.jp/sc-rules/c-msc22-c.html に挙がっているように、
if(i != setjmp(jb)){ ... }
のように式の中で比較するのは構わない。
 okuoku
okuoku
 じゃぁ何で longjmp は値を取るんだよ
具体的な値で比較することは違反ではないので、例えば longjmp 後の処理を switch による分岐で指定するみたいなユースケースがある。
switch(setjmp(jb)){ case 1: ...; }
 okuoku
okuoku代入が動作しないケースは存在するのか?
自分の知る限り、実際には setjmp の結果の代入ができないアーキテクチャは無い。これは、常識的なアーキテクチャでは基本的に setjmp は複数回returnする通常のC ABIの関数として実装されているためだと思う。つまり、 setjmp の呼出し側からは通常の関数に見えるため、代入を禁止するモチベーションが無い。
いわゆるSanitizer類はこのような利用をtrapできるかもしれない。
また、未定義挙動って事はコンパイラはそれを利用して最適化しても良いということでもあるので、正常動作は誰も保証してくれないということになる。
 okuoku
okuoku
 returns_twice 属性
gccやclangは returns_twice 属性を setjmp や vfork のような2度(以上)returnする関数に設定することを推奨している。gccは警告の制御くらいにしか言及していないが、ClangのバックエンドであるLLVMはtail-call最適化も無効にするとしている。
The
returns_twiceattribute tells the compiler that a function may return more than one time. The compiler ensures that all registers are dead before calling such a function and emits a warning about the variables that may be clobbered after the second return from the function. Examples of such functions aresetjmpandvfork. The longjmp-like counterpart of such function, if any, might need to be marked with thenoreturnattribute.
(ClangではなくLLVM言語のマニュアル -- ただgccの returns_twice 属性は結局LLVMのこのattributeにlowerされる)
This attribute indicates that this function can return twice. The C
setjmpis an example of such a function. The compiler disables some optimizations (like tail calls) in the caller of these functions.
呼出し側をtail-call最適化してしまうと、こんどは setjmp 呼出し側の関数を returns_twice として扱わなければならなくなる。そのように感染させていってしまうのはよくない; C言語の setjmp は、実際には setjmp した関数自体から2度(以上)returnすることは無い -- そもそも setjmp した関数から抜けたら longjmp してはいけない -- ため、 returns_twice を呼出し元にまで感染させていく必要性は本来存在しない。