gcc-4.8の変更点

相変わらずchangesに何も書かれてませんのでメモっときます。
4.7の時の変更が派手だったぶん、今回はおとなしいです。まあでも4.8で一番嬉しいのは-Ogと思う。

Ada2012のattributeの更なる実装

'Overlaps_Storageとか'Importとか'Conventionとか。めぼしいのはだいたい揃った感じ。

'Valueで例外発生時、エラーメッセージに引数の文字列が入るようになった

地味に便利と思います。

pragma Overflow_Checks

今までコマンドラインオプションでしかON/OFFできなかったオーバーフロー検出をソース中でON/OFFできるように。

pragma Loop_Optimize

http://gcc.gnu.org/onlinedocs/gnat_rm/Pragma-Loop_005fOptimize.html

Adaフロントエンドでも自動ベクトル化ですよ奥さん。

pragma No_Inline

要るよね。

pragma Warnings

でバックエンドの警告も操作できるように。

アドレス値の配置が自由に

今まで、アドレス値は必ずアライメントされてたんですが、x86みたいなアライメントが必須ではないプラットフォームではアライメントしなくてよくなりました。

protected型がロックフリーに

できるようになりました。
pragmaが必要です。(デフォルトではread-write lock)

protected Prot_Obj is
   pragma Lock_Free; -- undocumented
   ...
end Prot_Obj;

protectedがロックフリーになったんですか。エッグシェルモデルやめたってことですかね?

https://twitter.com/i_yudai/status/313838044199411714

エッグシェルモデルやめてcompare-and-swapになるっぽいですね。

訂正の訂正。本当にprotected型をロックフリーにできます。

バグ修正

とりあえず直ってるのを見つけられたのは

  • 2引数以上の時のuser-defined indexingのバグが直った。
  • StringとWide_Stringのオーバーロードコンパイラが落ちるバグが直った。
  • discriminantsまわりのバグが直った。

入ってないもの

  • raise expressionはAdaCore版GNATのDev Logにはあるのですが、gccには入ってません。
  • use all typeの実装は進展ないっぽいです。
  • 4.7でControlled型の実現方法が変わって、それ関係で結構バグがあったのですが、直ってないです。

headmaster 2013

URL → https://github.com/ytomino/headmaster/wiki/Document
前回 → http://d.hatena.ne.jp/ytqwerty/20101025#p1

というわけで良いタイミングですので(?)、headmasterの現状を書いておきます。

headmasterと先達プロジェクト

headmasterは、C言語を滅ぼすことを目的としたCのヘッダファイルのトランスレータです。やっていることはCILの四番煎じです。ただ、CILが入力、出力ともにC言語をメインとしているのに対し、headmasterはCのライブラリを他言語から使うためのヘッダファイル変換をメインとしています。

SWIGとの違いとしては、完全なプリプロセス/構文解析/意味解析をしていますので、所謂interface fileが不要です。その辺の.hをそのまま入力として扱えます。また、特にラッパーの生成などは行わず、1対1対応の直訳を基本としています。このため、ターゲット言語はCの関数を直接呼べる言語に限ります。まあ、今のところ変換先はAdaしか実装してないわけですが……。

GNAT GPL-fdump-ada-specとの違いは……正直あんまりないです。マクロの扱いやプラットフォームの差を吸収するための#pragmaがある点では今のところ勝ってますが、相手はgccに組み込んであるだけに、未実装部分が無いのと、あと一番大きいC++対応という点では負けています。C++のパーサなんて書きたくありませんのでこの点では負けっぱなし確定。

windows.h

そんなheadmasterも、ついに当初の目標に掲げていたwindows.hの変換に成功しました。

出力例 → http://panathenaia.halfmoon.jp/alang/headmaster/demo/windows_h/

この時は、大多数と言っても良いぐらいのリツィート/ふぁぼりをいただきありがとうございます。

三年前の時点では

しかしどのプロジェクトも、windows.hで記述されている意味や内容をすべて把握するには至っていません。

http://d.hatena.ne.jp/h_sakurai/20101026/p1

だったのですが、ここに技術革新が起きました!……というのは嘘で、別にwindows.hぐらいは意味解析までならCILもgcc -fdump-ada-specもとっくの昔に解析できてました(特に後者はgccその物なので当然ですね。h_sakuraiさん、黙っててごめんなさい)。windows.hは量が膨大なだけに軽く対応とは行きませんが、特にトリックめいた書き方がなされてたりするわけではありませんので。後は出力の問題。

そんなわけでトランスレータとしては、細かい点(windef.hとwinnt.h間の循環参照etc)さえ捌ければ、後は問題ないんですよ。難易度ではlibxml2やLinuxのヘッダーのほうが遥かに酷いです。ただ、CILは他言語への変換を目的にしておらず、gcc -fdump-ada-specはそうした個々のヘッダーに構うような小細工がされてない、という程度の差しかないです。

windows.h以外にも各OSの主要ヘッダーは大体いけます。このへん参照。

展望

で、まあ、まだまだ未実装部分も多いのですが、ある程度できたからには使って欲しいのが常です。
理想としては、ある程度活発な言語をサポートして、いろいろ勝手に開発してもらって、その成果をAda側で何もせずに享受する(Ada部分のユーザーは私一人で好きなように弄れる)、という風に持って行ければ……(夢は寝て見ろボケ)

  • Adaコミュニティに売り込む
    • gcc -fdump-ada-specがある、終了
  • Dコミュニティに売り込む
    • SWIG for Dがある、加えて主要ヘッダーは手動でポーティング済み、終了
  • Fortranコミュニティに売り込む
    • Fortranがこういうのを必要とするイメージが全く沸かない(偏見)

あれ、実は需要無い……?
……そんな皮算用よりリファクタリングが先です。今のコードは動くところまで持っていった段階ですので、汚いったらありゃしないです。人に見せるコードじゃありません。

OCaml Ada interoperability

adaside.ads *1

with System.Storage_Elements;
package adaside is
   subtype Value is System.Storage_Elements.Integer_Address;
   use type Value;
   function caml_string_length (S : Value) return System.Storage_Elements.Storage_Count;
   pragma Import (C, caml_string_length);
   function Val_long (X : Long_Integer) return Value is (Value (X) * 2 + 1);

   function The_Func (S : Value) return Value;
   pragma Export (C, The_Func, "ml_the_func");
end adaside;

adaside.adb

package body adaside is
   function The_Func (S : Value) return Value is
      Len : Natural := Natural (caml_string_length (S));
      Ada_S : String (1 .. Len);
      for Ada_S'Address use System.Storage_Elements.To_Address (S);
   begin
      return Val_long (Long_Integer'Value (Ada_S)); -- use 'Value attribute
   end The_Func;
end adaside;

ocamlside.ml

external the_func: string -> int = "ml_the_func";;

print_int (the_func "16#ff#");; (* hexadecimal form of Ada *)

コンパイルと実行

$ gcc -c -gnat2012 adaside.adb
$ ocamlopt ocamlside.ml adaside.o -ccopt /usr/local/lib/gcc/x86_64-apple-darwin10/4.7.2/adalib/libgnat.a
$ ./a.out
255

*1:ここではValue is Integer_Addressとしてますが、ビットフィールド風に定義することでVal_longやらLong_valを撤去できます。ていうかmlvalue.hでもそうなってて欲しい……。valueをそのまま演算するミスは誰でも一度はやらかしますよね!

導火線

こないだの記事に頂いたコメントです。

camlspotter 2013/02/27 12:50
そりゃ deforestation が無いから Some はどうしても allocate されるけどどう見ても short lived だし minor GC の範囲で気にするほどのものではないでしょう。それが気になるなら C で。

一方 option monad の bind chain が長くなって効率が気になる場合は例外を使いますね混ざっても気にしないというか混ざってほしいので。

http://d.hatena.ne.jp/ytqwerty/comment?date=20130226#c

いやまあいただいた意見そのものはありがたいですし、最初っから正直GC気にすんな以外の結論は無いと思ってましたので、それはそれでいいのですが……。

camlspotter 2013/02/27 12:50
そりゃ deforestation が無いから Some はどうしても allocate されるけどどう見ても short lived だし minor GC の範囲で気にするほどのものではないでしょう。それが気になるなら C で。

一方 option monad の bind chain が長くなって効率が気になる場合は例外を使いますね混ざっても気にしないというか混ざってほしいので。

http://d.hatena.ne.jp/ytqwerty/comment?date=20130226#c

(#^ω^)

よりにもよって私のブログでCを「ネイティブコードを主ターゲットに据えた(アセンブラ以外の高級)言語群」の代名詞として使うとは、私の導火線をよく判っておられます。

単に翌日の記事のネタ前振りですごめんなさい命ばかりは命ばかりはお助けくだされ(←小心者)

それと、でも、一昔前は「ネイティブコードランゲージ」とだけ言えばほとんどが生アドレスやインラインアセンブラが使えてやりたい放題な言語だったわけですが、今はそれこそOCaml等の関数型言語をはじめ、ネイティブコードは吐けるけど別にやりたい放題というわけではないものが増えてきてますので、このグループ(Fortran, C, Modula, Ada, D, etc)を表す適切な言葉がパッと思いつかないのも事実です。「システムプログラミング言語」ですとなんかピンと来ないんですよね。

Not_found

OCamlのコードを書くときにいつも迷っていることを書いてみます。超初心者質問。いい知恵ください。Not_foundの扱いです。
コンテナにデータがあった場合→データを使う、と無かった場合→データを新規に作る、で分岐する処理は頻出すると思います。

まず普通にif文。

if TABLE.mem key xs then
  let item = TABLE.find key xs in
  ...
else
  ...

これですと、探索を2回やってるわけで、遅そうです。
なので例外。

try
  let item = TABLE.find key xs in
  ... (*1*)
with Not_found -> ...

これですと、(*1*)部分に他にNot_foundを投げるような処理があった場合、例外をもみ消してしまいます。よろしくない。

なのでoption。

match (try Some (TABLE.find key xs) with Not_found -> None) with
| Some item ->
   ...
| None ->
   ...

これですと、Someのところでメモリアロケートが発生しています。こんなことで一時オブジェクトを作るのはGC的によろしくない。

例外の種類を差し替えるとか……。

try
  let item = try TABLE.find key xs with Not_found -> raise My_Not_found_2 in
  ... (*1*)
with My_Not_found_2 -> ...

……酷い無駄なコードな気がします。例外を投げたり受け止めたりというのは、普通に考えてメモリアロケートよりも重いわけで。

他の言語で行儀の悪いコードを書くなら、(*1*)でgotoしてとりあえずtryから出るようなことも可能なのですが、OCamlではそれはできません(ていうかOCaml文化以外の世界ならfindがNULLみたいな値を返して来ますよね、そもそも例外を使うような場面ではない)。一時オブジェクトを作ったりせずに、きれいにtryの範囲を限定するにはどうしたらいいでしょう。
え、一時オブジェクトを気にするな?いやまあそりゃあそうなんでしょうけど……。

__gnat_adjust_context_for_raiseの怪

これがわからない。

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50678

バグそのものは、Mountain Lionでの修正でそれまで__gnat_adjust_context_for_raiseに仕込んであったWorkaroundが不要になって、逆に悪さをするようになっていたというもの。Mountain Lionの時はWorkaroundを回避するようにしたら直った、らしい。

__gnat_adjust_context_for_raiseが何をする関数かといいますと、いくつかのプラットフォームで、シグナルハンドラから例外を投げるときに、sa_sigactionの第3パラメータとしてやってきたstruct ucontext *の補正を行うもの。

なんで補正が必要かというと、シグナルから復帰するためのucontextと、gccのunwinderが使うucontextが微妙に違うから。

うん、ここまではいいんですよ。

でもですよ、なんで、シグナルハンドラ中から投げるとはいえ、gccの例外がシグナルから復帰するためのstruct ucontext *に影響されるのかがわからないのです。

だって、このstruct ucontext *を補正した所で、そもそもこのstruct ucontext *の飛び先はシグナル発生箇所そのものなわけですよ。例外の飛び先は、もっとスタックを巻き戻した先にある例外ハンドラなわけですよ。_sigtramp云々書かれてますけど、_sigtramp中に戻るならともかく、例外はそんなところ飛び越えていくわけで。飛び先は現在のIPをDwrafのテーブルから検索して決めるわけで、レジスタの状態をどう戻すかも全部テーブルにあるわけですよ。
関係ないと思いませんか?
_Unwind_RaiseExceptionにこの情報が渡ってる気配のカケラも無いですし。
実際問題俺ランタイムなdrakeはこんな補正無くても動いてます。補正が必要とされてるLeopard〜Lionで。いや、こんな補正が本当に必要なら、世に無数にあるであろうシグナルを例外に変換しているアプリは(Darwinに限らず__gnat_adjust_context_for_raiseがなんかやってるプラットフォームで)軒並み怪しい動作をしているはず。

……ですけど現実にこれで挙動が変わるらしくて、冬の怪談です。