質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

アーキテクチャ

アーキテクチャとは、情報システム(ハードウェア、OS、アプリケーション、ネットワーク等)の設計方法、設計思想、設計思想に基づいて構築されたシステム構造をアーキテクチャと呼びます

Q&A

解決済

1回答

1705閲覧

レジスタの格納される位置

grape_ll

総合スコア83

アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

アーキテクチャ

アーキテクチャとは、情報システム(ハードウェア、OS、アプリケーション、ネットワーク等)の設計方法、設計思想、設計思想に基づいて構築されたシステム構造をアーキテクチャと呼びます

0グッド

0クリップ

投稿2021/06/05 12:24

編集2021/06/06 06:02

###条件
下に示すcファイルをアセンブリにしたものを考えます.
このとき,w0, w1がx29が示すアドレスから見てどの位置に格納されているのかを見ます.
8個までレジスタ経由で引数を渡すものとします.
9個目以降はスタック経由で渡します.ただし,今回は引数はふたつまでしか出てきていません.
結果はw0レジスタにセットします.
###質問内容

私の考えでは,21行目のstpは,二つのレジスタx29, x30の内容をスタックポインタ(sp)から16だけ減じたアドレスのメモリに書き込んでいるので,引数はそのx29の直下に位置するのではないかと考えていますが,あっていますでしょうか.
図的には,汎用レジスタ1がx29であり,そのすぐ下にw1, w0の順に入っていると考えています.
イメージ説明

###cファイル

C

1sum(int a, int b) 2{ 3 return a+b; 4} 5 6main() 7{ 8 int a; 9 a= sum(1, 2); 10 put_int(a); 11} 12

###アセンブリ
イメージ説明

###追記
tlcの実行結果

FuncTab sum #1 main #2 SymTab id(1) a #1, offset(-4) b #2, offset(-8) id(2) a #1, offset(-4) root func[ identifier(r0)(sum)] ( param(r0)( identifier(r0)(a)) param(r0)( identifier(r0)(b))) l(3): return( add(r0)( identifier(r0)(a) identifier(r1)(b))) func[ identifier(r0)(main)] () l(8): declaration( identifier(r0)(a)) l(9): stm_asign( exp_asign(r0)( identifier(r0)(a) call(r1)( identifier(r0)(sum) ( const_int(r0)(1) const_int(r0)(2))))) l(10): stm_asign( call(r0)( identifier(r0)(put_int) ( identifier(r0)(a))))

引数が9個のプログラム

C

1sum(int a, int b, int c, int d, int e, int f,int g,int h,int i) 2{ 3 return a+b+c+d+e+f+g+h+i; 4} 5 6main() 7{ 8 int a; 9 a= sum(1, 2,3,4,5,6,7,8,9); 10 put_int(a); 11} 12
1 .arch armv8-a 2 .file "nine_var.c" 3 .text 4 .align 2 5 .global sum 6 .type sum, %function 7 sum: 8 .LFB0: 9 .cfi_startproc 10 add w0, w0, w1 11 add w0, w0, w2 12 add w0, w0, w3 13 add w0, w0, w4 14 add w0, w0, w5 15 add w0, w0, w6 16 add w0, w0, w7 17 ldr w1, [sp] 18 add w0, w0, w1 19 ret 20 .cfi_endproc 21 .LFE0: 22 .size sum, .-sum 23 .align 2 24 .global main 25 .type main, %function 26 main: 27 .LFB1: 28 .cfi_startproc 29 sub sp, sp, #32 30 .cfi_def_cfa_offset 32 31 stp x29, x30, [sp, 16] 32 .cfi_offset 29, -16 33 .cfi_offset 30, -8 34 add x29, sp, 16 35 mov w0, 9 36 str w0, [sp] 37 mov w7, 8 38 mov w6, 7 39 mov w5, 6 40 mov w4, 5 41 mov w3, 4 42 mov w2, 3 43 mov w1, 2 44 mov w0, 1 45 bl sum 46 bl put_int 47 mov w0, 0 48 ldp x29, x30, [sp, 16] 49 add sp, sp, 32 50 .cfi_restore 29 51 .cfi_restore 30 52 .cfi_def_cfa_offset 0 53 ret 54 .cfi_endproc 55 .LFE1: 56 .size main, .-main 57 .ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0" 58 .section .note.GNU-stack,"",@progbits

これのtlcによる実行

FuncTab sum #1 main #2 SymTab id(1) a #1, offset(-12) b #2, offset(-16) c #3, offset(-20) d #4, offset(-24) e #5, offset(-28) f #6, offset(-32) g #7, offset(-36) h #8, offset(-40) i #9, offset(-8) id(2) a #1, offset(-4) root func[ identifier(r0)(sum)] ( param(r0)( identifier(r0)(a)) param(r0)( identifier(r0)(b)) param(r0)( identifier(r0)(c)) param(r0)( identifier(r0)(d)) param(r0)( identifier(r0)(e)) param(r0)( identifier(r0)(f)) param(r0)( identifier(r0)(g)) param(r0)( identifier(r0)(h)) param(r0)( identifier(r0)(i))) l(3): return( add(r0)( add(r0)( add(r0)( add(r0)( add(r0)( add(r0)( add(r0)( add(r0)( identifier(r0)(a) identifier(r1)(b)) identifier(r1)(c)) identifier(r1)(d)) identifier(r1)(e)) identifier(r1)(f)) identifier(r1)(g)) identifier(r1)(h)) identifier(r1)(i))) func[ identifier(r0)(main)] () l(8): declaration( identifier(r0)(a)) l(9): stm_asign( exp_asign(r0)( identifier(r0)(a) call(r1)( identifier(r0)(sum) ( const_int(r0)(1) const_int(r0)(2) const_int(r0)(3) const_int(r0)(4) const_int(r0)(5) const_int(r0)(6) const_int(r0)(7) const_int(r0)(8) const_int(r0)(9))))) l(10): stm_asign( call(r0)( identifier(r0)(put_int) ( identifier(r0)(a))))

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/06/05 12:41

実際に出力されているアセンブリコード(x86)と、考えているアーキテクチャが一致していないようです。「STP」という命令もアセンブリリストにありません。
SaitoAtsushi

2021/06/05 12:42

「21行目のstp」とは何のことですか? 提示されているコードの中にはありません。 また、提示されているアセンブリコードは x86 (32bit) のものらしく見えますが、 STP というニーモニックはおそらく Arm のものではないでしょうか?
grape_ll

2021/06/05 12:59

申し訳ございません. 間違えたものを貼ってしまいました,編集いたします. ご指摘ありがとうございます.
guest

回答1

0

ベストアンサー

事情と質問の意図が見えてきたので、回答修正します。修正続きですみません。

そのすぐ下にw1, w0の順に入っていると考え

これは間違い。W0, W1 はCPUのレジスタであって、メモリではありません。CPUレジスタ X0~X7 或いは W0~W7はレジスタそのものです。

関数sumの中の二つの引数はメモリに格納されていません。アセンブリコードに示されているように、
「add w0, w0, w1」は「w0 = w0 + w1」という命令が、レジスタだけで計算を行い、すぐ w0 の値を return しています。

引数はそのx29の直下に位置するのではないかと考えていますが,あっていますでしょうか

この質問は、もし関数の引数がスタックメモリも使うようになったら、それはX29(が格納された番地)の次の番地か?という質問であれば、そうなる可能性が高い・・・いや、スタックトップから X29, X30 とプッシュされるからX30がプッシュされた番地の次の番地、即ちX30の次がスタック渡しされた引数である可能性が高い

STP命令の直後に「mov x29, sp」命令があります。これによりスタックポインタSPとX29は、共にスタックにプッシュされた X29 (の値があるメモリ)の番地を指しています。
スタック渡しされた引数(9番目の引数)は「SP + 16」番地もしくは「X29 + 16」番地にある、どちらでもアクセスが可能です。私の勝手な予想だと、フレームポインタ X29 を使ってアクセスするのではないかな。それは9個以上の引数を持つ関数をコンパイルしてみればはっきりするでしょう。

なお、引数を用意する=引数をスタックにプッシュするのは、関数を呼出す側のコードです。呼ばれた関数から見れば、既にそこ(スタックの先頭付近のメモリ)に引数がセットされているという事。

質問にあるように、ARM64は8個の引数までレジスタで渡すようなので、9個目の引数を持つ関数をコンパイルして、アセンブリコードを調べてみればよいだけ。人に聞くより自分で試してみれば良いのに、なぜそうしないのだろうかと思う。今私の手元にARM64用コンパイラが無いので確かめられないが、持っているならすぐ確認できるはず。

要するに、実際の所を具体的に知りたければ、次のようなことを試してみる。

  • もっとたくさんの引数を持つ関数をコンパイルして、生成されるアセンブリコードを調べる
  • サイズが大きめの構造体を引数にする関数をコンパイルして(以下略)
  • (中級以上なら)可変長引き数の関数を書いて(以下略)

念のため:STP命令がスタックに書き込んでいるX29, X30レジスタは

  • X29 関数のスタックフレームを指すフレームポインタ
  • X30 関数の戻り番地が格納されるリンクレジスタ

なので、X86-64と対応させると

  • X29はRBPレジスタに対応する
  • X30は(call命令でスタックにプッシュされる)戻り番地に対応する

スタックの使い方はCPUが違ってもそんなに違わない。また、そうするためにSTP命令があるようです。


引数9個の関数 sum() 関数のコンパイル結果からわかること。
sum() 関数は

  • 8個の引数 w0 ~ w7 を、メモリに格納することなく計算に使う。
  • 9番目の引数はスタックトップから読み出す(ldr w1,[sp])。これは私の予想と違いました。その理由はsum() がリーフ関数(後述)であることが大きいかもしれません。

その関数を呼び出すmain()でわかったこと。main()がやっていることは

  • スタック領域に32バイトの領域をつくる(sub sp, sp, #32)。
  • スタックトップから16バイト目のところ(32バイト領域の中ほど)に X29, X30を退避する(stp x29,x30,[sp, 16])。
  • スタックトップ(から16バイト)を sum() 関数に渡す引数領域として使う(str w0,[sp])。ただし、この場合w0は32bitなので実際に使っているのは16バイト中4バイトだけ。
  • 残り8個の引数はレジスタ w0 ~ w7 で渡す(予想通り)。

sum() 関数は他の関数をcallしない関数です。こういう関数を、関数呼び出しの関係を樹形図になぞらえて、リーフ関数(leaf function、葉関数)と呼びます。リーフ関数は関数の戻り番地(X30)をメモリに退避する必要がありません。そのため sum() の中で STP 命令を実行しません。その結果、main() が用意した9番目の引数は、そのままスタックトップにある([sp]にある)という状態です。


リーフ関数でない関数、関数の中に配列などを持つ関数などもコンパイルしてみると面白そうですね。

投稿2021/06/05 15:54

編集2021/06/06 09:19
rubato6809

総合スコア1380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

grape_ll

2021/06/06 01:13

確認が遅れてしまい申し訳ございません.分かりやすいように修正していただきありがとうございます. >CPUレジスタ X0~X7 或いは W0~W7はレジスタそのものです。 ということは,関数sumの中の二つの引数はメモリに格納されていないということでしょうか. 興味のある人は,関数samの二つの引数がx29の指すアドレスを機転としてどの位置に格納されているかを確認してみるとより理解が深まるといわれたので,てっきりこのw0, w1のレジスタもメモリ上にあるのだと思っていました. >X30の次がスタック渡しされた引数である可能性が高い。 スタック渡しでx30の下に来たとして,stpではオフセットが16であり,その確保した分はx29とx30で使い切ってしまうように思えるのですが,それでも良いのでしょうか.
rubato6809

2021/06/06 02:24

回答修正しました。 > その確保した分はx29とx30で使い切ってしまう それはその通り。16はX29とX30の分です。でも引数はそこに続くメモリに既にセットされてあります。
grape_ll

2021/06/06 02:43 編集

回答の追記,ありがとうございます. >スタック渡しされた引数(9番目の引数)は「SP + 16」番地もしくは「X29 + 16」番地にある      ーーーーーー      | |      ーーーーーー <-x29      | |      ーーーーーー <-x30      | |      ーーーーーー <-移動前のsp      | | <-ここに引数 ビジュアル的に見るとこのような理解であっていますでしょうか.
rubato6809

2021/06/06 02:54

たぶん、そう。でも私はコンパイルされたARM64のコードを見ていないので、ご自分でコンパイルして確かめることですね。
grape_ll

2021/06/06 02:58

後ほど引数9個の関数を作って実行してみます. 結果を追記いたしますので,見ていただいても構いませんでしょうか.
grape_ll

2021/06/06 05:54

tlcという簡易コンパイラで実行してみると追記に記したようになるのですが,もしかしてこれのoffsetが場所を示していますでしょうか.
grape_ll

2021/06/06 11:07

回答の修正,ありがとうございます. 色々なことをつかめた気がします.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問