Adaの定数の話
Cだと#define
するような、ハードコーディングしてメモリに直接乗せたくないような定数ってAdaでどうやって書くの? というお話。もっと言うと普遍整数型と数値宣言とconstって中でどうなってるのよという話。
まずはCで書いてみる。メモリに定数を乗っけるコード。
main() {
int x = 17;
int v = x;
return v;
}
gcc -S -O0で最適化されていないアセンブラを吐いてみる。重要そうなところだけを抜き出すとこんな感じ。
main:
.LFB2:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movl $17, -8(%rbp)
movl -8(%rbp), %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %eax
leave
ret
次に同じコードをAdaで書いてみる。
function Integer_Test_Normal return Integer is
X : Integer := 17;
V : Integer;
begin
V := X;
return V;
end Integer_Test_Normal;
_ada_integer_test_normal:
.LFB3:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movl $17, -8(%rbp)
movl $17, -4(%rbp)
movl $17, %eax
leave
ret
いきなり予想外の結果に。X
とV
に17が直接代入されている。何でだろう。
ためしに、宣言部で初期化するのをやめてみる。
function Integer_Test_Normal return Integer is
X : Integer;
V : Integer;
begin
X := 17;
V := X;
return V;
end Integer_Test_Normal;
しかし結果変わらず。フロントエンドがなんかしてるのかもしれない。まぁ変数が二つちゃんと出来てるのはわかるからいいや。
つぎに、17を#define
してみる。
#define X 17
main() {
int v = X;
return v;
}
コンパイルするとこんな感じ。変数が1個になってる。
main:
.LFB2:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movl $17, -4(%rbp)
movl -4(%rbp), %eax
leave
ret
さて、Adaで同じように書くにはどうするべきか。とりあえず普遍整数で行ってみよう。
function Integer_Test_Universal return Integer is
X : constant := 17;
V : Integer;
begin
V := X;
return V;
end Integer_Test_Universal;
_ada_integer_test_universal:
.LFB3:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movl $17, -4(%rbp)
movl $17, %eax
leave
ret
お、良さそうな感じ(相変わらず代入してないのが気になるけど)。次は普通のconstant
で実験。
function Integer_Test_Constant return Integer is
X : constant Integer := 17;
V : Integer;
begin
V := X;
return V;
end Integer_Test_Constant;
_ada_integer_test_constant:
.LFB3:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movl $17, -8(%rbp)
movl $17, -4(%rbp)
movl $17, %eax
leave
ret
予想通り、変数2つ。というのも、Adaのconstant
は単に2回目の代入が出来ない変数なので、値自体は実行時に決まる事を考えればこうなるでしょう。
というわけで、#define
の代わりは普遍整数を使うのがいいようだ。しかし、整数以外の値を定数にしたい場合はどうしたものだろう。
ついでにもうちょっと実験。数値宣言した型のconstだとどうなるのかな。
function Integer_Test_Range return Integer is
type X_Range is range 17 .. 17;
X : constant X_Range := 17;
V : Integer;
begin
V := Integer(X);
return V;
end Integer_Test_Range;
_ada_integer_test_range:
.LFB3:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movb $17, -5(%rbp)
movl $17, -4(%rbp)
movl $17, %eax
leave
ret
まぁ、そりゃそうか、という結果ですね。それにしても、アセンブリで見ると数値宣言がメモリ領域をちゃんと計算してくれているのがわかりますね。まぁ、そうなってないとIntegerすらろくに使えなくなっちゃうので当然ですが。というのも、Integerは計算機が計算しやすい大きさの数値宣言があらかじめ定義されてるだけなんですね。
俺は40bitの符号無し整数が欲しいんだよ、って時は適当に作れちゃいますよ。
procedure Integer_Test_Range40 is
type Unsigned_Intger40 is range 0 .. 2 ** 40;
X : constant Unsigned_Intger40 := 17;
begin
null;
end Integer_Test_Range40;
_ada_integer_test_range40:
.LFB3:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movq $17, -8(%rbp)
leave
ret
内部的には64bitになってますね。内部表現説を使うにしてもprimitiveの場合2のpowerじゃないといけないので32bitの次は必然的に64bitになるわけですね。