teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

6

sub関数をsum()関数に、ついでに一文追加

2021/06/06 09:19

投稿

rubato6809
rubato6809

スコア1382

answer CHANGED
@@ -34,7 +34,7 @@
34
34
 
35
35
  ---
36
36
 
37
- 引数9個の関数 sub() 関数コンパイルしてわかったこと。
37
+ 引数9個の関数 sum() 関数コンパイル結果からわかこと。
38
38
  sum() 関数は
39
39
  - 8個の引数 w0 ~ w7 を、メモリに格納することなく計算に使う。
40
40
  - 9番目の引数はスタックトップから読み出す(ldr w1,[sp])。これは私の予想と違いました。その理由はsum() がリーフ関数(後述)であることが大きいかもしれません。
@@ -45,4 +45,8 @@
45
45
  - スタックトップ(から16バイト)を sum() 関数に渡す引数領域として使う(str w0,[sp])。ただし、この場合w0は32bitなので実際に使っているのは16バイト中4バイトだけ。
46
46
  - 残り8個の引数はレジスタ w0 ~ w7 で渡す(予想通り)。
47
47
 
48
- sum() 関数は他の関数をcallしない関数です。こういう関数を、関数呼び出しの関係を樹形図になぞらえて、リーフ関数(leaf function、葉関数)と呼びます。リーフ関数は関数の戻り番地(X30)をメモリに退避する必要がありません。そのため sum() の中で STP 命令を実行しません。その結果、main() が用意した9番目の引数は、そのままスタックトップにある([sp]にある)という状態です。
48
+ sum() 関数は他の関数をcallしない関数です。こういう関数を、関数呼び出しの関係を樹形図になぞらえて、リーフ関数(leaf function、葉関数)と呼びます。リーフ関数は関数の戻り番地(X30)をメモリに退避する必要がありません。そのため sum() の中で STP 命令を実行しません。その結果、main() が用意した9番目の引数は、そのままスタックトップにある([sp]にある)という状態です。
49
+
50
+ ---
51
+
52
+ リーフ関数でない関数、関数の中に配列などを持つ関数などもコンパイルしてみると面白そうですね。

5

引数9個の関数をコンパイルした結果について

2021/06/06 09:18

投稿

rubato6809
rubato6809

スコア1382

answer CHANGED
@@ -30,4 +30,19 @@
30
30
  なので、X86-64と対応させると
31
31
  - X29はRBPレジスタに対応する
32
32
  - X30は(call命令でスタックにプッシュされる)戻り番地に対応する
33
- スタックの使い方はCPUが違ってもそんなに違わない。また、そうするためにSTP命令があるようです。
33
+ スタックの使い方はCPUが違ってもそんなに違わない。また、そうするためにSTP命令があるようです。
34
+
35
+ ---
36
+
37
+ 引数9個の関数 sub() 関数をコンパイルしてわかったこと。
38
+ sum() 関数は
39
+ - 8個の引数 w0 ~ w7 を、メモリに格納することなく計算に使う。
40
+ - 9番目の引数はスタックトップから読み出す(ldr w1,[sp])。これは私の予想と違いました。その理由はsum() がリーフ関数(後述)であることが大きいかもしれません。
41
+
42
+ その関数を呼び出すmain()でわかったこと。main()がやっていることは
43
+ - スタック領域に32バイトの領域をつくる(sub sp, sp, #32)。
44
+ - スタックトップから16バイト目のところ(32バイト領域の中ほど)に X29, X30を退避する(stp x29,x30,[sp, 16])。
45
+ - スタックトップ(から16バイト)を sum() 関数に渡す引数領域として使う(str w0,[sp])。ただし、この場合w0は32bitなので実際に使っているのは16バイト中4バイトだけ。
46
+ - 残り8個の引数はレジスタ w0 ~ w7 で渡す(予想通り)。
47
+
48
+ sum() 関数は他の関数をcallしない関数です。こういう関数を、関数呼び出しの関係を樹形図になぞらえて、リーフ関数(leaf function、葉関数)と呼びます。リーフ関数は関数の戻り番地(X30)をメモリに退避する必要がありません。そのため sum() の中で STP 命令を実行しません。その結果、main() が用意した9番目の引数は、そのままスタックトップにある([sp]にある)という状態です。

4

コメントに対する回答を追加

2021/06/06 08:18

投稿

rubato6809
rubato6809

スコア1382

answer CHANGED
@@ -2,13 +2,21 @@
2
2
 
3
3
  > そのすぐ下にw1, w0の順に入っていると考え
4
4
 
5
- **これは間違い**。W0, W1 はCPUのレジスタであって、メモリではありません。
5
+ **これは間違い**。W0, W1 はCPUのレジスタであって、メモリではありません。CPUレジスタ X0~X7 或いは W0~W7はレジスタそのものです。
6
- CPUレジスタ X0~X7 或いは W0~W7はレジスタそのものです。
7
6
 
7
+ 関数sumの中の二つの引数はメモリに格納されていません。アセンブリコードに示されているように、
8
+ 「add w0, w0, w1」は「w0 = w0 + w1」という命令が、レジスタだけで計算を行い、すぐ w0 の値を return しています。
9
+
8
10
  > 引数はそのx29の直下に位置するのではないかと考えていますが,あっていますでしょうか
9
11
 
10
12
  この質問は、もし関数の引数がスタックメモリも使うようになったら、それはX29(が格納された番地)の次の番地か?という質問であれば、そうなる可能性が高い・・・いや、スタックトップから X29, X30 とプッシュされるからX30がプッシュされた番地の次の番地、即ち**X30の次がスタック渡しされた引数である可能性が高い**。
11
13
 
14
+ STP命令の直後に「mov x29, sp」命令があります。これによりスタックポインタSPとX29は、共にスタックにプッシュされた X29 (の値があるメモリ)の番地を指しています。
15
+ スタック渡しされた引数(9番目の引数)は「SP + 16」番地もしくは「X29 + 16」番地にある、どちらでもアクセスが可能です。私の勝手な予想だと、フレームポインタ X29 を使ってアクセスするのではないかな。それは9個以上の引数を持つ関数をコンパイルしてみればはっきりするでしょう。
16
+
17
+ なお、引数を用意する=引数をスタックにプッシュするのは、関数を呼出す側のコードです。呼ばれた関数から見れば、既にそこ(スタックの先頭付近のメモリ)に引数がセットされているという事。
18
+
19
+
12
20
  質問にあるように、ARM64は8個の引数までレジスタで渡すようなので、9個目の引数を持つ関数をコンパイルして、アセンブリコードを調べてみればよいだけ。人に聞くより自分で試してみれば良いのに、なぜそうしないのだろうかと思う。今私の手元にARM64用コンパイラが無いので確かめられないが、持っているならすぐ確認できるはず。
13
21
 
14
22
  要するに、実際の所を具体的に知りたければ、次のようなことを試してみる。

3

回答修正

2021/06/06 02:21

投稿

rubato6809
rubato6809

スコア1382

answer CHANGED
@@ -1,9 +1,13 @@
1
- 事情と質問の意図が見えてきたので、回答修正します。
1
+ 事情と質問の意図が見えてきたので、回答修正します。修正続きですみません。
2
2
 
3
+ > そのすぐ下にw1, w0の順に入っていると考え
4
+
5
+ **これは間違い**。W0, W1 はCPUのレジスタであって、メモリではありません。
6
+ CPUレジスタ X0~X7 或いは W0~W7はレジスタそのものです。
7
+
3
8
  > 引数はそのx29の直下に位置するのではないかと考えていますが,あっていますでしょうか
4
9
 
5
- この質問は、もし関数の引数がスタックメモリも使うようになったら、それはX29(が格納された番地)の次の番地か?という質問のようですね
10
+ この質問は、もし関数の引数がスタックメモリも使うようになったら、それはX29(が格納された番地)の次の番地か?という質問であれば、そなる可能性が高い・・・いや、スタックトップから X29, X30 とプッシュされるからX30がプッシュされた番地の次の番地、即ち**X30の次がスタック渡しされた引数ある可能性が高い**
6
- そうなる可能性が高い。
7
11
 
8
12
  質問にあるように、ARM64は8個の引数までレジスタで渡すようなので、9個目の引数を持つ関数をコンパイルして、アセンブリコードを調べてみればよいだけ。人に聞くより自分で試してみれば良いのに、なぜそうしないのだろうかと思う。今私の手元にARM64用コンパイラが無いので確かめられないが、持っているならすぐ確認できるはず。
9
13
 

2

可変長引き数の事を追加したついでに細かい修正

2021/06/06 00:19

投稿

rubato6809
rubato6809

スコア1382

answer CHANGED
@@ -2,19 +2,20 @@
2
2
 
3
3
  > 引数はそのx29の直下に位置するのではないかと考えていますが,あっていますでしょうか
4
4
 
5
- この質問は、もし関数の引数がスタックメモリも使うようになったら、それはX29(が格納された番地)の次の番地か?という質問なんですね。
5
+ この質問は、もし関数の引数がスタックメモリも使うようになったら、それはX29(が格納された番地)の次の番地か?という質問のようですね。
6
6
  そうなる可能性が高い。
7
7
 
8
- 質問にあるように、ARM64は8個の引数までレジスタで渡すようなので、9個目の引数を持つ関数をコンパイルして、アセンブリコードを調べてみればよいだけ。
9
- 人に聞くより自分で試してみれば良いのに、なぜそうしないのだろうかと思う。今私の手元にARM64用コンパイラが無いので確かめられないが、持っているならすぐ確認できるはず。
8
+ 質問にあるように、ARM64は8個の引数までレジスタで渡すようなので、9個目の引数を持つ関数をコンパイルして、アセンブリコードを調べてみればよいだけ。人に聞くより自分で試してみれば良いのに、なぜそうしないのだろうかと思う。今私の手元にARM64用コンパイラが無いので確かめられないが、持っているならすぐ確認できるはず。
10
9
 
11
10
  要するに、実際の所を具体的に知りたければ、次のようなことを試してみる。
12
11
  - もっとたくさんの引数を持つ関数をコンパイルして、生成されるアセンブリコードを調べる
13
12
  - サイズが大きめの構造体を引数にする関数をコンパイルして(以下略)
13
+ - (中級以上なら)可変長引き数の関数を書いて(以下略)
14
14
 
15
15
  念のため:STP命令がスタックに書き込んでいるX29, X30レジスタは
16
16
  - X29 関数のスタックフレームを指すフレームポインタ
17
17
  - X30 関数の戻り番地が格納されるリンクレジスタ
18
18
  なので、X86-64と対応させると
19
19
  - X29はRBPレジスタに対応する
20
- - X30は(call命令でスタックにプッシュされる)戻り番地に対応する
20
+ - X30は(call命令でスタックにプッシュされる)戻り番地に対応する
21
+ スタックの使い方はCPUが違ってもそんなに違わない。また、そうするためにSTP命令があるようです。

1

事情と質問の意図が見えてきたので、修正

2021/06/05 23:12

投稿

rubato6809
rubato6809

スコア1382

answer CHANGED
@@ -1,9 +1,14 @@
1
+ 事情と質問の意図が見えてきたので、回答修正します。
2
+
1
3
  > 引数はそのx29の直下に位置するのではないかと考えていますが,あっていますでしょうか
2
4
 
5
+ この質問は、もし関数の引数がスタックメモリも使うようになったら、それはX29(が格納された番地)の次の番地か?という質問なんですね。
3
- らく間違い。
6
+ うなる可能性が高い。
4
- 「x29の直下」とはスタックメモリのことだと思いますが、アセンブリコードに現れている引き数 W0, W1 はCPUのレジスタであって、メモリではありません。もっとも、引数の個数が多くなればスタックメモリも使いますが、ARM64は引数に割当てられるレジスタの個数が多い(正確な個数は知らない)ので、引数はレジスタだけで渡せる場合が多いと思います。
5
7
 
8
+ 質問にあるように、ARM64は8個の引数までレジスタで渡すようなので、9個目の引数を持つ関数をコンパイルして、アセンブリコードを調べてみればよいだけ。
9
+ 人に聞くより、自分で試してみれば良いのに、なぜそうしないのだろうか、と思う。今私の手元にARM64用コンパイラが無いので確かめられないが、持っているならすぐ確認できるはず。
10
+
6
- 実際の所を具体的に知りたければ、次のようなことを試してみたらいかが
11
+ 要するに、実際の所を具体的に知りたければ、次のようなことを試してみ
7
12
  - もっとたくさんの引数を持つ関数をコンパイルして、生成されるアセンブリコードを調べる
8
13
  - サイズが大きめの構造体を引数にする関数をコンパイルして(以下略)
9
14