浮動小数点数を扱うRISC-Vコードを出力
RISC-Vで浮動小数点数をロードする方法がさっぱり分からないので、いつも通りCのソースから生成したアセンブリを眺める。
code:float_test.c
int main() {
const float pi = 3.14;
float f = pi;
printf("%f\n", f);
return 0;
}
code:float_test.s
.file "float_test.c"
.text
.section .rodata
.align 2
.LC1:
.string "%f\n"
.text
.align 2
.globl main
.type main, @function
main:
addi sp,sp,-48
sw ra,44(sp)
sw s0,40(sp)
addi s0,sp,48
lui a5,%hi(.LC0)
flw fa5,%lo(.LC0)(a5)
fsw fa5,-20(s0)
flw fa5,-20(s0)
fsw fa5,-24(s0)
flw fa5,-24(s0)
fcvt.d.s fa5,fa5
fsd fa5,-40(s0)
lw a2,-40(s0)
lw a3,-36(s0)
lui a5,%hi(.LC1)
addi a0,a5,%lo(.LC1)
call printf
li a5,0
mv a0,a5
lw ra,44(sp)
lw s0,40(sp)
addi sp,sp,48
jr ra
.size main, .-main
.section .rodata
.align 2
.LC0:
.word 1078523331
.ident "GCC: () 12.2.0"
ここで浮動小数点数 pi を定義して、
code:float_test.s (asm)
.LC0:
.word 1078523331
ここで pi をレジスタへロードしてるっぽい
code:float_test.s (asm)
lui a5,%hi(.LC0)
flw fa5,%lo(.LC0)(a5)
変更箇所
code:diff
diff --git a/RV32/asm.ml b/RV32/asm.ml
index 48ae39d..e085eaf 100644
--- a/RV32/asm.ml
+++ b/RV32/asm.ml
@@ -85,7 +85,14 @@ let regs = (* Array.init 27 (fun i -> Printf.sprintf "_R_%d" i) *)
"%s1"; "%s2"; "%s3"; "%s4"; "%s5"; "%s6"; "%s7"; "%s8"; "%s9";
"%t1"; "%t2"; "%t3"; "%t4"; "%t5"; "%t6"
|]
-let fregs = Array.init 32 (fun i -> Printf.sprintf "%%f%d" i)
+let fregs =
+ [|
+ "%fa0"; "%fa1"; "%fa2"; "%fa3"; "%fa4"; "%fa5"; "%fa6"; "%fa7";
+ "%fs0"; "%fs1"; "%fs2"; "%fs3"; "%fs4"; "%fs5"; "%fs6"; "%fs7";
+ "%fs8"; "%fs9"; "%fs10"; "%fs11";
+ "%ft0"; "%ft1"; "%ft2"; "%ft3"; "%ft4"; "%ft5"; "%ft6"; "%ft7";
+ "%ft8"; "%ft9"; "%ft10"; "%ft11"
+ |]
let allregs = Array.to_list regs
let allfregs = Array.to_list fregs
let reg_cl = regs.(Array.length regs - 1) (* closure address (caml2html: sparcasm_regcl) *)
diff --git a/RV32/emit.ml b/RV32/emit.ml
index e867ce5..7b6c6d6 100644
--- a/RV32/emit.ml
+++ b/RV32/emit.ml
@@ -2,6 +2,7 @@ open Asm
external gethi : float -> int32 = "gethi"
external getlo : float -> int32 = "getlo"
+external getf : float -> int32 = "getf"
let stackset = ref S.empty (* すでにSaveされた変数の集合 (caml2html: emit_stackset) *)
let stackmap = ref [] (* Saveされた変数の、スタックにおける位置 (caml2html: emit_stackmap) *)
@@ -67,8 +68,8 @@ and g' oc = function (* 各命令のアセンブリ生成 (caml2html: emit_gprim
Printf.fprintf oc "\tlui %s, %d\n" r n;
Printf.fprintf oc "\taddi %s, %s, %d\n" r r m
| 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)
+ Printf.fprintf oc "\tlui %s, %%hi(%s)\n" (reg reg_tmp) l;
+ Printf.fprintf oc "\tflw %s, %%lo(%s)(%s)\n" (reg x) l (reg reg_tmp)
| NonTail(x), SetL(Id.L(y)) ->
let s = load_label x y in
Printf.fprintf oc "%s" s
@@ -295,13 +296,12 @@ let h oc { name = Id.L(x); args = _; fargs = _; body = e; ret = _ } =
let f oc (Prog(data, fundefs, e)) =
Format.eprintf "generating assembly...@.";
if data <> [] then
- (Printf.fprintf oc "\t.data\n\t.literal8\n";
+ (Printf.fprintf oc "\t.data\n";
List.iter
(fun (Id.L(x), d) ->
- Printf.fprintf oc "\t.align 3\n";
+ Printf.fprintf oc "\t.align 2\n";
Printf.fprintf oc "%s:\t # %f\n" x d;
- Printf.fprintf oc "\t.long\t%ld\n" (gethi d);
- Printf.fprintf oc "\t.long\t%ld\n" (getlo d))
+ Printf.fprintf oc "\t.long\t%ld\n" (getf d))
data);
Printf.fprintf oc "\t.text\n";
Printf.fprintf oc "\t.globl min_caml_start\n";
diff --git a/float.c b/float.c
index 1165150..78a80c1 100644
--- a/float.c
+++ b/float.c
@@ -19,3 +19,14 @@ value getlo(value v) {
d.d = Double_val(v);
return caml_copy_int32(d.i1); }
+
+typedef union {
+ float f;
+} flt;
+
+value getf(value v) {
+ flt f;
+ f.f = Double_val(v);
+ return caml_copy_int32(f.i0); +}
dataセクションへpiを追加する所
オリジナルのコードは浮動小数点数の pi を data セクションに格納する際に double (8バイト) で格納していたが、float (4バイト) で良いので以下のように修正した。
code:diff
let f oc (Prog(data, fundefs, e)) =
Format.eprintf "generating assembly...@.";
if data <> [] then
- (Printf.fprintf oc "\t.data\n\t.literal8\n";
+ (Printf.fprintf oc "\t.data\n";
List.iter
(fun (Id.L(x), d) ->
- Printf.fprintf oc "\t.align 3\n";
+ Printf.fprintf oc "\t.align 2\n";
Printf.fprintf oc "%s:\t # %f\n" x d;
- Printf.fprintf oc "\t.long\t%ld\n" (gethi d);
- Printf.fprintf oc "\t.long\t%ld\n" (getlo d))
+ Printf.fprintf oc "\t.long\t%ld\n" (getf d))
getf が必要になったので追加
code:diff
diff --git a/float.c b/float.c
index 1165150..78a80c1 100644
--- a/float.c
+++ b/float.c
@@ -19,3 +19,14 @@ value getlo(value v) {
d.d = Double_val(v);
return caml_copy_int32(d.i1); }
+
+typedef union {
+ float f;
+} flt;
+
+value getf(value v) {
+ flt f;
+ f.f = Double_val(v);
+ return caml_copy_int32(f.i0); +}
レジスタへ浮動小数点数を読み込み
以下のような感じでレジスタへ浮動小数点数を読み込む。
code: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)
+ Printf.fprintf oc "\tlui %s, %%hi(%s)\n" (reg reg_tmp) l;
+ Printf.fprintf oc "\tflw %s, %%lo(%s)(%s)\n" (reg x) l (reg reg_tmp)
print_floatがうまくいかない問題
print_float で 0.00000 が出力されて困っていたんだけど、MinCamlからは ft0 に引数を渡して、Cのmin_caml_print_float では fa0 を出力していたのが原因だった。
MinCamlはfregs の頭から関数の引数として使うため、CのABIに合わせるため fa0 fa1 ... の順に並べるようにした。(fa 以降は適当に並べた)
code:diff
diff --git a/RV32/asm.ml b/RV32/asm.ml
index 48ae39d..e085eaf 100644
--- a/RV32/asm.ml
+++ b/RV32/asm.ml
@@ -85,7 +85,14 @@ let regs = (* Array.init 27 (fun i -> Printf.sprintf "_R_%d" i) *)
"%s1"; "%s2"; "%s3"; "%s4"; "%s5"; "%s6"; "%s7"; "%s8"; "%s9";
"%t1"; "%t2"; "%t3"; "%t4"; "%t5"; "%t6"
|]
-let fregs = Array.init 32 (fun i -> Printf.sprintf "%%f%d" i)
+let fregs =
+ [|
+ "%fa0"; "%fa1"; "%fa2"; "%fa3"; "%fa4"; "%fa5"; "%fa6"; "%fa7";
+ "%fs0"; "%fs1"; "%fs2"; "%fs3"; "%fs4"; "%fs5"; "%fs6"; "%fs7";
+ "%fs8"; "%fs9"; "%fs10"; "%fs11";
+ "%ft0"; "%ft1"; "%ft2"; "%ft3"; "%ft4"; "%ft5"; "%ft6"; "%ft7";
+ "%ft8"; "%ft9"; "%ft10"; "%ft11"
+ |]
let allregs = Array.to_list regs
let allfregs = Array.to_list fregs
let reg_cl = regs.(Array.length regs - 1) (* closure address (caml2html: sparcasm_regcl) *)
(メモ)RV32での各型のサイズを確認