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型をロックフリーにできます。
バグ修正
とりあえず直ってるのを見つけられたのは
入ってないもの
- 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はそうした個々のヘッダーに構うような小細工がされてない、という程度の差しかないです。
展望
で、まあ、まだまだ未実装部分も多いのですが、ある程度できたからには使って欲しいのが常です。
理想としては、ある程度活発な言語をサポートして、いろいろ勝手に開発してもらって、その成果を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
Adaと64bit呼び出し規約とlldiv
lldivを呼ぶ関数を書いてたらちょっと驚きました。
こういうの。
procedure call_lldiv (x, y : long_long_integer; q, r : out long_long_integer) is type lldiv_t is record q, r : long_long_integer; end record; pragma Convention (C, lldiv_t); function lldiv (x, y : long_long_integer) return lldiv_t; pragma Import (C, lldiv, "lldiv"); result : lldiv_t; begin result := lldiv (x, y); q := result.q; r := result.r; end call_lldiv;
最適化してコンパイルする。
gcc -S -O3 call_lldiv.adb
すると……
.text .align 4,0x90 .globl __ada_call_lldiv __ada_call_lldiv: LFB1: subq $40, %rsp LCFI0: call _lldiv addq $40, %rsp LCFI1: ret
えっ。
GNATフロントエンドのスカラー値のoutパラメータの扱いは参照渡しではなくて所謂multi-value returnで、%rax、%rdxで返すっぽいです。で、64ビット呼び出し規約では小さな構造体もレジスタ返しってことになっているらしくて、lldivも値を%rax、%rdxで返すっぽくて、引数並びも一致してるから何もせずにcallして何もせずにretすることになったようです。
ですもんで-m32ですとこうなる。
.text .align 4,0x90 .globl __ada_call_lldiv __ada_call_lldiv: LFB1: pushl %esi LCFI0: subl $56, %esp LCFI1: movl 76(%esp), %eax leal 32(%esp), %ecx movl 80(%esp), %edx movl %ecx, (%esp) movl 64(%esp), %esi movl %eax, 12(%esp) movl 68(%esp), %eax movl %edx, 16(%esp) movl 72(%esp), %edx movl %eax, 4(%esp) movl %edx, 8(%esp) call _lldiv LCFI2: subl $4, %esp LCFI3: movl 32(%esp), %eax movl 36(%esp), %edx movl %eax, (%esi) movl 40(%esp), %eax movl %edx, 4(%esi) movl 44(%esp), %edx movl %eax, 8(%esi) movl %edx, 12(%esi) movl %esi, %eax addl $56, %esp LCFI4: popl %esi LCFI5: ret $4
で、これを見てしまうと、どうせならスタックを$40ほど取って戻してしてるのも最適化して欲しくて、そうすれば-foptimize-sibling-callsも働いてjmp一個にできるはずなんですけれど、このスタックが強敵で色々オプションを試しても消えてくれません。なんでー?
Ovenモドキ妄想
gcc-4.7ではAda2012のAda.Iterator_Interfacesが実装されました。
コンテナをfor文で回すためのものですが、要するに所謂"range"そのものですので、Ovenの様に連鎖させることもできるはずです。(まあこんなもの無くてもシグネチャを全部自分で決めてしまえば当然どうにでもなりますが、枠組みが標準に入ったのは大きいです)
用語の違い補足。C++のiterator=AdaのCursor、C++のrange=AdaのIterator、次の位置を求める操作はC++ではiteratorの++ですがAdaでは(Cursorではなくて)IteratorのNext関数。forward iteratorみたいな種類分けは(Cursorではなくて)Iterator側。
generic with package Source_Iterators is new Ada.Iterator_Interfaces (others => <>); package Generic_Filters is -- FilterがTrueを返したCursorのみ抽出 function Iterate ( Source : Source_Iterators.Forward_Iterator'Class; Filter : not null access function (Position : Source_Iterators.Cursor) return Boolean) return Source_Iterators.Forward_Iterator'Class; private -- 実装はこれを読んでいるあなたに任せた end Generic_Filters;
↑みたいなのを用意しておけば、↓みたいに使えるはずです。
declare package Int_Lists is new Ada.Containers.Doubly_Linked_Lists (Integer); package Int_Filters is new Generic_Filters (Int_Lists.List_Iterator_Interfaces); function Even (X : Int_Lists.Cursor) return Boolean is (Element (X) rem 2 = 0); List : Int_Lists.List; begin for I in Int_Filters.Iterate (Int_Lists.Iterate (List), Even'Access) loop Put (Element (I)); -- 偶数だけ出てくる end loop; end;
(実はGNATはgenericのformal packageのothers関係が怪しいので、このままですと通らなかったりしますがCursorとHas_Elementを個別のパラメータにしてやれば通ります。タイプ量倍増orz さっさと直らないでしょうか→ http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47748)
で、まあ、こういうのってライブラリとしては面白い反面、意外と使いどころがなかったりするのですが、妄想した範囲で実用的っぽいのは、Unicodeのややこしい操作を単純化するのに使えないかなー、とかなんとか。
-- Iterate_Code_Pointsは文字列を取ってコードポイント単位のイテレータを返す関数 for I in Iterate_Code_Points (S, Substitude => ' ') loop S (I.First .. I.Last) -- slicing To_Wide_Wide_Character (I) -- code point Has_Error (I) -- TrueならUTF-*としておかしい end loop;
-- Iterate_Combiningはコードポイント単位のイテレータを取って -- Combining Character Sequence単位のイテレータを返す関数 for I in Iterate_Combining (Iterate_Code_Points (S)) loop S (I.First .. I.Last) -- slicing Compose (I) -- NFCに Decompose (I) -- NFDに end loop;
-- Iterate_Translatingはコードポイント単位のイテレータを取って -- Wide_Wide_Character_Mappingを適用する関数 for I in Iterate_Translating ( Iterate_Code_Points (S), Ada.Strings.Wide_Wide_Maps.Wide_Wide_Constant.Upper_Case_Map) loop Value (I) -- Ada.Strings.Wide_Wide_Maps.Value (Upper_Case_Map, ...) end loop;
これなら、先頭から順次NFCに変換した上でCase Foldingして比較、みたいな処理も簡単に書ける気がします。
どなたか実装しません?