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をそのまま演算するミスは誰でも一度はやらかしますよね!

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一個にできるはずなんですけれど、このスタックが強敵で色々オプションを試しても消えてくれません。なんでー?

振り返ってみる

「Ada言語の現状」という検索キーワードでここにたどり着いた方がおられるらしい。ふと振り返ってみると、私が最初にAdaに触れてから、随分とAdaも変わりました。昔語りをしてみましょう。

続きを読む

GNAT開発環境について〜gcc付属ツール

2012年12月時点でのgccまたはGNAT GPLをインストールした際に付属する「公式」ツール群の紹介を行う。
多岐に渡るので、一つ一つの詳しい説明は行わない。
各ツールの細かい情報はそれぞれのドキュメントを参照して欲しい。

もし知らないツール名があったらちょっと読んでみて欲しい。
もしかしたらあなたの問題を解決するツールがあるかもしれないから。

★は重要度。五点満点。

なおこの記事は某所のパクリです。あとAda Advent Calendarとは何の関係もありません。

続きを読む

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して比較、みたいな処理も簡単に書ける気がします。

どなたか実装しません?