不動小数点数を扱うAArch64コードを出力
サンプルコード
code:ml
let a = 3.1415 in
let b = int_of_float a in
print_int b;
print_newline ()
ビルドして
code:sh
make clean && ./to_aarch64 && make min-caml && ./min-caml samples/float_test && clang samples/float_test.s libmincaml.S samples/stub.c -o samples/a.out && samples/a.out
実行結果
code:sh
$ samples/a.out
sp = 0x16d1ce828, hp = 0x120008000
3
指定ラベルからの浮動小数点数の読み込み
指定したラベルから浮動小数点数を読み込むことができない...(l.7 から浮動小数点数をレジスタに読み込みたい)
code:sh
make clean && ./to_aarch64 && make min-caml && ./min-caml samples/float_test && clang samples/float_test.s libmincaml.S samples/stub.c -o samples/a.out && samples/a.out
code:asm
.data
.literal8
.align 3
l.7: # 3.141500
.long -1065151889
.long 1074340298
.text
.globl _min_caml_start
.align 2
_min_caml_start: # main entry point
add x28, x0, 0
add x27, x1, 0
add x28, x28, 16
# main program starts
# *** FLi: l.7 ***
ldr x26, l.7
mov x26, lr
add x28, x28, 8
bl _min_caml_int_of_float
sub x28, x28, 8
mov lr, x26
mov x26, lr
add x28, x28, 8
bl _min_caml_print_int
sub x28, x28, 8
mov lr, x26
mov x26, lr
add x28, x28, 8
bl _min_caml_print_newline
sub x28, x28, 8
mov lr, x26
# main program ends
sub x28, x28, 16
ret
以下のCのコードから生成したアセンブリコードを参考にしてみる。
code:float_test.c
float pi = 3.14;
int main() {
float f = pi;
printf("%f\n", f);
return 0;
}
code:sh
gcc -S float_test.c
code:float_test.s
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 12, 0 sdk_version 13, 1
.globl _main ; -- Begin function main
.p2align 2
_main: ; @main
.cfi_startproc
; %bb.0:
stp x29, x30, sp, #32 ; 16-byte Folded Spill .cfi_def_cfa w29, 16
.cfi_offset w30, -8
.cfi_offset w29, -16
adrp x8, _pi@PAGE
fcvt d0, s0
mov x8, sp
adrp x0, l_.str@PAGE
add x0, x0, l_.str@PAGEOFF
bl _printf
ldp x29, x30, sp, #32 ; 16-byte Folded Reload ret
.cfi_endproc
; -- End function
.section __DATA,__data
.globl _pi ; @pi
.p2align 2
_pi:
.long 0x4048f5c3 ; float 3.1400001
.section __TEXT,__cstring,cstring_literals
l_.str: ; @.str
.asciz "%f\n"
.subsections_via_symbols
上記のコードを参考に、浮動小数点数読み込みを実装したのが以下のコード。
code:AArch64/emit.ml.diff
| NonTail(x), FLi(Id.L(l)) ->
- let s = load_label (reg reg_tmp) l in
- Printf.fprintf oc "%s\tlfd\t%s, 0(%s)\n" s (reg x) (reg reg_tmp)
+ (* ラベル l に格納された浮動小数点数をレジスタへロードする *)
+ Printf.fprintf oc "\tadrp %s, %s@PAGE\n" (reg reg_tmp) l;
+ Printf.fprintf oc "\tldr %s, %s, %s@PAGEOFF\n" (reg x) (reg reg_tmp) l | NonTail(x), SetL(Id.L(y)) ->
min_caml_int_of_float
int_of_float で浮動小数点数の整数部分を取り出したい。
AArch64にも FJCVTZS があるらしいので、それを利用すると良さそう
こんな感じ。
code:asm
# int_of_float, truncate
.text
.align 2
.globl _min_caml_int_of_float
_min_caml_int_of_float:
.globl _min_caml_truncate
_min_caml_truncate:
fjcvtzs w0, d0
ret
他にも使えそうなのがある
fcvtzs
fjcvtzs
code:test/float.ml
(*
このテストを実行する場合は、Main.file等を呼び出す前に
Typing.extenvを:=等で書き換えて、あらかじめsinやcosなど
外部関数の型を陽に指定する必要があります(そうしないと
MinCamlでは勝手にint -> intと推論されるため)。
*)
print_int
(int_of_float
((sin (cos (sqrt (abs_float (-12.3))))
+. 4.5 -. 6.7 *. 8.9 /. 1.23456789)
*. float_of_int 1000000))
code:sh
make clean && ./to_aarch64 && make min-caml && ./min-caml test/float && clang test/float.s libmincaml.S samples/stub.c -o test/a.out && test/a.out
出力されたエラーをつぶしていく。
Typing.extenvでの型の指定
(* このテストを実行する場合は、Main.file等を呼び出す前に
Typing.extenvを:=等で書き換えて、あらかじめsinやcosなど
外部関数の型を陽に指定する必要があります(そうしないと
MinCamlでは勝手にint -> intと推論されるため)。 *)
sin (float -> float)
cos (float -> float)
sqrt (float -> float)
abs_float (float -> float)
float_of_int (int -> float)
int_of_float (float -> int)
code:ml
(M.add "sin" Type.Float M.empty);;
(M.add "sqrt" Type.Float (M.add "cos" Type.Float (M.add "sin" Type.Float (M.empty))));;
型を指定しない場合
code:ml
let ic = open_in "test/float.ml" in
let b = Buffer.create 0 in
let src = (Buffer.add_channel b ic (in_channel_length ic); Buffer.contents b) in
(Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token (Lexing.from_string src)))));;
- : KNormal.t =
KNormal.Let (("Ti17.18", Type.Int),
KNormal.Let (("Td16.19", Type.Float),
KNormal.Let (("Td13.20", Type.Float),
KNormal.Let (("Td7.23", Type.Float),
KNormal.Let (("Td5.29", Type.Float),
KNormal.Let (("Ti4.31", Type.Int),
KNormal.Let (("Ti3.32", Type.Int),
KNormal.Let (("Ti2.33", Type.Int),
KNormal.Let (("Td1.34", Type.Float), KNormal.Float (-12.3),
KNormal.ExtFunApp ("abs_float", "Td1.34")), KNormal.Let (("Td6.30", Type.Float), KNormal.Float 4.5,
KNormal.FAdd ("Td5.29", "Td6.30"))),
KNormal.Let (("Td12.24", Type.Float),
KNormal.Let (("Td10.25", Type.Float),
KNormal.Let (("Td8.27", Type.Float), KNormal.Float 6.7,
KNormal.Let (("Td9.28", Type.Float), KNormal.Float 8.9,
KNormal.FMul ("Td8.27", "Td9.28"))),
KNormal.Let (("Td11.26", Type.Float), KNormal.Float 1.23456789,
KNormal.FDiv ("Td10.25", "Td11.26"))),
KNormal.FSub ("Td7.23", "Td12.24"))),
KNormal.Let (("Td15.21", Type.Float),
KNormal.Let (("Ti14.22", Type.Int), KNormal.Int 1000000,
KNormal.ExtFunApp ("float_of_int", "Ti14.22")), KNormal.FMul ("Td13.20", "Td15.21"))),
KNormal.ExtFunApp ("int_of_float", "Td16.19")), 型を指定した場合
code:ml
let ic = open_in "test/float.ml" in
let b = Buffer.create 0 in
let src = (Buffer.add_channel b ic (in_channel_length ic); Buffer.contents b) in
let _ = Typing.extenv := (M.add "sqrt" (Type.Fun (Type.Float, Type.Float)) (M.add "cos" (Type.Fun (Type.Float, Type.Float)) (M.add "sin" (Type.Fun (Type.Float, Type.Float)) (M.empty)))) in (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token (Lexing.from_string src)))));;
参考
FP to GP