回答編集履歴

5

スタック領域での割り当てられ方について補足

2019/10/19 03:00

投稿

raccy
raccy

スコア21735

test CHANGED
@@ -20,7 +20,13 @@
20
20
 
21
21
  - スタックへ変数は連続して詰まれるが、アライメントによりその場所に置けない場合は、置ける場所までずれる。
22
22
 
23
- ※ 質問のコードからちょっと変更を加えた物ですが、 [https://godbolt.org/z/Ch2Uzw](https://godbolt.org/z/Ch2Uzw) と [https://godbolt.org/z/hKTXFR](https://godbolt.org/z/hKTXFR) を見比べればわかると思います。`int`はrbpとの差が4の倍数であれば置けますが、`double`は8の倍数の所にしか置けないため、二つ目のコードの`x`について[rpb-12]ではなく[rbp-16]になっています。なお、どちらも、変数毎にスタックへpushするのでは無く、最初に32バイトのスタック領域が確保するようにしています(**x86_64だからこうなのかは不明**)。**なぜ一つ目のコードが24バイトでは無く32バイトなのかはわかりません**。x86_64でのスタック領域確保は16バイト単位とか縛りがあるんでしょうか?詳しい人教えてください。
23
+ ※ 質問のコードからちょっと変更を加えた物ですが、 [https://godbolt.org/z/Ch2Uzw](https://godbolt.org/z/Ch2Uzw) と [https://godbolt.org/z/hKTXFR](https://godbolt.org/z/hKTXFR) を見比べればわかると思います。`int`はrbpとの差が4の倍数であれば置けますが、`double`は8の倍数の所にしか置けないため、二つ目のコードの`x`について[rpb-12]ではなく[rbp-16]になっています。
24
+
25
+ - 実際の所、x86_64のほとんどのコンパイラでは、ローカル変数毎にpushするのではなく、関数開始時にローカル変数が十分入る大きさのスタック領域を確保し、その領域に対してローカル変数を割り当てると言うことをしている。ただし、上から割り当てるのか、下から割り当てるのかはコンパイラによって異なる。GCCは上から下に向かって割り当てていく(pushで実装していた時代の名残と思われますが、ここら辺の歴史に詳しい人がいたら教えてください)。
26
+
27
+ ※ 前の項でどちらも、変数毎にスタックへpushするのでは無く、最初に32バイトのスタック領域が確保するようにしているのがわかると思います。ただ、**なぜ一つ目のコードが24バイトでは無く32バイトなのかはわかりません**。x86_64でのスタック領域確保は16バイト単位とか縛りがあるんでしょうか?詳しい人教えてください。
28
+
29
+
24
30
 
25
31
 
26
32
 

4

条件がどんどん増える

2019/10/19 03:00

投稿

raccy
raccy

スコア21735

test CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
 
26
26
 
27
- 上記のことからx86_64で、GCCで最適化オプション無しの場合、上のアセンブリの結果も参考にすると、
27
+ 上記のことからx86_64で、GCCで最適化オプション無しの場合(ただし一部のOSでは異なる場合がある)、上のアセンブリの結果も参考にすると、
28
28
 
29
29
 
30
30
 
@@ -40,7 +40,7 @@
40
40
 
41
41
 
42
42
 
43
- となります。異なるアーキテクチャ、コンパイラオプション、定義の順番が異なるコードでは違う動作になります。例えば、[clangに変更しただけ](https://godbolt.org/z/1uySIq)でも4,12,8という差になります。clangだと最初に4バイト確保しているようで、その分ズレて、`x`の時にアライメントにあわせてズレてしまったため、`b`と`x`の差がGCCと異なったと言うことです。この最初の4バイトは何かというと・・・すいません、調べてもわかりませんでした。
43
+ となります。異なるアーキテクチャ、OS、コンパイラ、コンパイラオプション、定義の順番が異なるコードでは違う動作になります。例えば、[clangに変更しただけ](https://godbolt.org/z/1uySIq)でも4,12,8という差になります。clangだと最初に4バイト確保しているようで、その分ズレて、`x`の時にアライメントにあわせてズレてしまったため、`b`と`x`の差がGCCと異なったと言うことです。この最初の4バイトは何かというと・・・すいません、調べてもわかりませんでした。
44
44
 
45
45
 
46
46
 

3

コンパイラだけの話ではないので

2019/10/18 12:11

投稿

raccy
raccy

スコア21735

test CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  - x86やx86_64等の主に使われているCISCのCPUでは、スタック領域は仮想メモリ空間の後ろに配置され、前の方向に向かって詰まれていく(詰まれた分だけ番地は減る)。つまり、スタックにpushすればスタックの先頭を指し示すレジスタ上のスタックポインタは前に進み(減る)、popすればスタックポインタは後ろに進む(増える)。(RISCのCPUではそもそもスタック領域やスタックポインタが用意されていなかったり、スタックが終わりの方向に詰まれる(詰まれた分だけ番地は増える)というのもあるらしい)
12
12
 
13
- - 最適化を有効にしない(最適化無し、GCCであれば`-O0`をつけるようなもの)場合は、ほとんどのコンパイラでは、ローカル変数は定義した順番でスタックに詰まれていく。(最適化無効でもスタックに詰まれる順番が定義した順になるとは限らないという場合もあるようです。コメントを参考にしてください。)
13
+ - 最適化を有効にしない(最適化無し、GCCであれば`-O0`をつけるようなもの)場合は、ほとんどのコンパイラや環境では、ローカル変数は定義した順番でスタックに詰まれていく。(最適化無効でもスタックに詰まれる順番が定義した順になるとは限らないという場合もあるようです。コメントを参考にしてください。)
14
14
 
15
15
  - 変数に対して、アドレス演算子など変数のアドレスそのものに対する処理が含まれる場合は、最適化が有効でも必ずメモリ領域上に確保される。(n1570のどこかに書いてあったはず。逆に言うとそうでは無い変数はレジスタ上に現れるだけだったりするということ)
16
16
 

2

スタックに順番に詰まれない場合がある。

2019/10/18 12:07

投稿

raccy
raccy

スコア21735

test CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  - x86やx86_64等の主に使われているCISCのCPUでは、スタック領域は仮想メモリ空間の後ろに配置され、前の方向に向かって詰まれていく(詰まれた分だけ番地は減る)。つまり、スタックにpushすればスタックの先頭を指し示すレジスタ上のスタックポインタは前に進み(減る)、popすればスタックポインタは後ろに進む(増える)。(RISCのCPUではそもそもスタック領域やスタックポインタが用意されていなかったり、スタックが終わりの方向に詰まれる(詰まれた分だけ番地は増える)というのもあるらしい)
12
12
 
13
- - 最適化を有効にしない(最適化無し、GCCであれば`-O0`をつけるようなもの)場合は、ローカル変数は定義した順番でスタックに詰まれていく。(最適化無効でもスタックに詰まれる順番が定義した順になるとは限らないというコンパイラがあれば教えてください。)
13
+ - 最適化を有効にしない(最適化無し、GCCであれば`-O0`をつけるようなもの)場合は、ほとんどのコンパイラでは、ローカル変数は定義した順番でスタックに詰まれていく。(最適化無効でもスタックに詰まれる順番が定義した順になるとは限らないという場合もあるようです。トを参考にしてください。)
14
14
 
15
15
  - 変数に対して、アドレス演算子など変数のアドレスそのものに対する処理が含まれる場合は、最適化が有効でも必ずメモリ領域上に確保される。(n1570のどこかに書いてあったはず。逆に言うとそうでは無い変数はレジスタ上に現れるだけだったりするということ)
16
16
 

1

スタック領域が存在しないアーキテクチャもあるので、ちょっと表現を変えた。

2019/10/18 11:56

投稿

raccy
raccy

スコア21735

test CHANGED
@@ -6,9 +6,9 @@
6
6
 
7
7
 
8
8
 
9
- - ローカル(自動)変数がメモリ上に領域を確保する場合は、スタック領域に詰まれる。
9
+ - ローカル(自動)変数がメモリ上に領域を確保する場合は、スタック領域またはスタック用としてコンパイラが指定した領域スタックとして詰まれる。
10
10
 
11
- - x86やx86_64等の主に使われているCISCのCPUでは、スタックは仮想メモリ空間の後ろから前の方向に向かって詰まれていく(詰まれた分だけ番地は減る)。つまり、スタックにpushすればスタックの先頭を指し示すレジスタ上のスタックポインタは前に進み(減る)、popすればスタックポインタは後ろに進む(増える)。(RISCのCPUではそもそもスタックポインタが用意されていなかったり、スタックが終わりの方向に詰まれる(詰まれた分だけ番地は増える)というのもあるらしい)
11
+ - x86やx86_64等の主に使われているCISCのCPUでは、スタック領域は仮想メモリ空間の後ろに配置され、前の方向に向かって詰まれていく(詰まれた分だけ番地は減る)。つまり、スタックにpushすればスタックの先頭を指し示すレジスタ上のスタックポインタは前に進み(減る)、popすればスタックポインタは後ろに進む(増える)。(RISCのCPUではそもそもスタック領域やスタックポインタが用意されていなかったり、スタックが終わりの方向に詰まれる(詰まれた分だけ番地は増える)というのもあるらしい)
12
12
 
13
13
  - 最適化を有効にしない(最適化無し、GCCであれば`-O0`をつけるようなもの)場合は、ローカル変数は定義した順番でスタックに詰まれていく。(最適化無効でもスタックに詰まれる順番が定義した順になるとは限らないというコンパイラがあれば教えてください。)
14
14