-foptimize-sibling-callsは別名定義ができる言語には必須ですよねえ

本当は末尾再帰の最適化をするためのものであろうこの最適化オプション、-O3でも含まれていないのですが、Adaのような言語には必須です。昨日気付いたばっかですが。
たとえば、次のような状態を考えてみてください。

package A is
   procedure P;
end A;
with A;
package B is
   procedure R renames A.P;
end B;

B.Rへの呼び出しは、直接A.Pへの呼び出しとなります。
しかし、上記の書き方では、with B;したソースをコンパイルする時は、必然的にA.adsも読まれることになってしまい、まあCのヘッダじゃあるまいし名前空間の汚染なんて起きないんですが、コンパイル時間が若干かかるようになるのは事実です。
それで次のようにします。

package A is
   procedure P;
end A;
package B is
   procedure R;
end B;
with A;
package body B is
   procedure R renames A.P;
end B;

すると、B.Rの名前でコールする側は、B.Rの実体がわからないので、B.Rをシンボルとして使うしかありません。ということでB.Rというシンボルが必要になります。
B.Rの実体としては単にjmp A.Pでいいんですが、何しろバックエンドはgccですから、真面目に引数をコピーしてはcallしてespを戻してくれます。A.Pが大量の引数を取る関数だったら、そのオーバーヘッドたるや莫大です。
で、-foptimize-sibling-callsを付けておけば、jmp A.Pになります。まあそれだけの話です。
それでも何しろバックエンドはgccですから、-fomit-frame-pointerも一緒に付けない限り、push ebpして即座にpop ebpという無意味なスタックフレームの生成が残ります。

Win32構造化例外処理その1

最近ここに何か書く暇があまり無くて調べたらすぐわかるようなAdaネタでお茶を濁していたわけですが、気がつくと何も書けなくなってきている気がしてきたので、リハビリがてらになにか短期シリーズやろうと思いました。
ネタは……探すか……きょろきょろ……うろうろ……(ネットサーフィンで半日経過)……はっ。いかんいかん、ええと、Binary Hacks読書中つながりのhttp://www.kmonos.net/wlog/67.html#_0031061124経由で、構造化例外処理です。
構造化例外処理というのはC++の例外処理とは異なり↓です。

__try {  ... } __except(GetExceptionCode() == 0x...){ ... }

嘘です。
この場合__tryも、__exceptも、VC++の拡張キーワードに過ぎないわけで、要するに普通のtry文とコンパイラの扱いは同じです。これ止まりの解説は意外に多いのですが、こんなのは「VC++C++ではなくCコンパイラとして使ってそれでも例外処理をしたい場合」以外は役に立ちません。
たとえば、http://d.hatena.ne.jp/ytqwerty/20060123#p1のような、構文に無い制御をアセンブラ埋めこみで後づけした場合、それを例外安全にしたければどうすればいいでしょうか?
……の前に、基本書いておきますか。
Win32では、例外は、(setjumpやDWARF-2利用のzero-cost-exceptionなどと違って)OSレベルで実現されています。アクセス違反やゼロ割算も「Win32の例外」として飛んできます。それでも、アクセス違反やゼロ割算なんて、受け止めて処理するような例外ではないわけですから、即落ちでいい、と考えられるかもしれません。そういうコンパイラは多いです。実際、Win32の構造化例外処理をまるで考えていないコンパイラは多いです。特にgのつくやつとその派生系。
アクセス違反やゼロ割算なんかは即落ちでいいのですが、受け止められることを期待した例外というものもあります。
たとえばDelphiでは、次のExitは例外で実現されます。……せんが、Win32例外処理の構造をなぞることでfinallyは実行されます。

procedure A;
begin
  try
    Exit; {Exit専用の例外が発生<ins>……しているわけではない(汗)</ins>}
  finally
    Beep; {実行される!}
  end;
end; {<del>ここで受け止められる</del><ins>ハンドラをなぞった後でここまでふつーにjmpしてくる</ins>}

あー…ええと…例外ハンドラの登録は所詮スタックとリストの操作に過ぎませんので同じ関数の中でハンドラの配置がわかっているならいくらでも最適化が効きます。gdgd
これに限らず、一般に、tryブロックを超えてのジャンプは、一旦例外を経由する実装が多いです。昔は多かったんですよっ!……記憶が定かならっ!……調べれば調べるほど、記憶が疑わしくなってきましたがっ!
他にも、コールバック関数から一連の処理を中断させるために例外を投げる使い方もあります。Win32 APIEnumなんちゃらはそれで中断できます。しかし、「Win32の構造化例外処理をまるで考えていないコンパイラで書かれたコールバック関数を受け取る関数」から呼ばれたコールバック関数で同じことをすると、暴走してくれるかもしれません。
Win32では、広域脱出を行いたい時は、setjumpではなくて構造化例外処理が作法ですので、対応できるものならしておくが吉です。
ああそうそう、先のリンクの「DelphiC#のyieldモドキ」は、Delphi for Win32のfor .. inが、元々IEnumeratorを開放するために例外ハンドラを使う仕様ですので、Breakに対応した時点で例外も大丈夫というか対応方法は同じというか、ともかく大丈夫の筈です。
つづく。
追記 tryブロックからの脱出
ずっと後と思ってたんですけど調べてみました。

最近のDelphi → System.@TryFinallyExit
C++Builder10 → __return_unwind
VC++Toolkit → __local_unwind2
dmd → finallyブロックを直接call

FreePascal → setjump使ってた;; (所詮これもg系か……)

例外投げてる奴無いなあ;;
Delphi5まで遡ったんですが、その頃は既に@TryFinallyExit存在してました。あまりにも情報古すぎるぞ俺。

gccのAdaランタイム改造

それはそうとhttp://panathenaia.halfmoon.jp/alang/gcc-private-build/ですが、改造は着実に進んでまして、既に入出力関連は普通に使いそうなものは全部UTF-8になっています。
特にpragma Wide_Character_Encodingの追加されたgcc-4.3系では、s-wch_*を実行ファイルに含まれないようにしていて、バイナリ中の文字列表現をUTF-8で統一しています。-gnatWは文字列にのみ作用します。-gnatiは無視します。そして、これらのオプション無しでもいきなりUTF-8で識別子を宣言できます。
あと、Ada.Text_IOは、出力ではUTF-8をDBCSに、入力ではDBCSをUTF-8に変換します。コンソール上ではSet_ColやSet_Lineでカーソル移動ができるおまけつきです。
ファイルI/Oも、改行云々含めて諸悪の根源っぽいfopenを使わずに、直接CreateFile(W)するようにしてしまってますし。
いやあ、苦労しました。ははは……(忙しかったんちゃうんかい、というツッコミは受けつけません)
残すはWide_Text_IOとWide_Wide_Text_IOぐらいなのですが……ぶっちゃけ、要るのか、これ……?