回答:
スタックの増加は通常、オペレーティングシステム自体に依存するのではなく、実行しているプロセッサに依存します。たとえば、Solarisはx86とSPARCで動作します。Mac OSX(前述のとおり)はPPCおよびx86で動作します。Linuxは、仕事中の大きなhonkin 'System zから、ちっぽけな小さな腕時計まで、あらゆるもので動作します。
CPUが任意の種類の選択を提供する場合、OSで使用されるABI /呼び出し規約により、コードで他のすべてのコードを呼び出す場合にどの選択を行う必要があるかが指定されます。
プロセッサとその方向は次のとおりです。
これらの最後の数人に私の年齢を示すと、1802は初期のシャトルを制御するために使用されたチップでした(ドアが開いているかどうかを検知しました、それが持っていた処理能力に基づいて、私は疑います:-)と私の2番目のコンピューター、COMX-35(私のZX80に続く)。
PDP11の詳細はここから収集され、8051の詳細はここから収集されます。
SPARCアーキテクチャは、スライディングウィンドウレジスタモデルを使用します。アーキテクチャ上で表示される詳細には、有効で内部的にキャッシュされるレジスタウィンドウの循環バッファも含まれ、オーバーフローまたはアンダーフローが発生するとトラップが発生します。詳細はこちらをご覧ください。以下のようSPARCv8マニュアルが説明し、SAVEおよびRESTORE命令はADD命令のようなもので、プラス登録ウィンドウの回転を。通常の負の代わりに正の定数を使用すると、上向きに成長するスタックが得られます。
前述のSCRT手法は別の手法です。1802は、SCRT(標準の呼び出しと戻りの手法)に16ビットの16個のレジスタをいくつか使用しました。1つはプログラムカウンターで、SEP Rn
命令を備えたPCとして任意のレジスタを使用できます。1つはスタックポインターで、2つは常にSCRTコードアドレスを指すように設定されています。1つは呼び出し用、もう1つは戻り用です。レジスターは特別な方法で扱われませんでした。これらの詳細はメモリからのものであり、完全に正しくない場合があることに注意してください。
たとえば、R3がPC、R4がSCRT呼び出しアドレス、R5がSCRT戻りアドレス、R2が「スタック」(ソフトウェアに実装されている引用符)の場合、SEP R4
R4をPCに設定し、SCRTの実行を開始します。呼び出しコード。
その後、R2「スタック」(私はR6は、一時保管のために使用されたと思います)にR3を保存し、それを調整したり、ダウン、R3、次の2つのバイトをつかむ、それらをロードするに、次に行う、R3 SEP R3
および新しいアドレスで実行されています。
戻るにはSEP R5
、R2スタックから古いアドレスをプルし、2を追加して(呼び出しのアドレスバイトをスキップする)、それをR3にロードしSEP R3
て、前のコードの実行を開始します。
すべての6502/6809 / z80スタックベースのコードの後、最初に頭を回すのは非常に困難ですが、壁にぴったりの方法でエレガントです。また、チップの大きな売りの特徴の1つは、16個の16ビットレジスタのフルスイートでしたが、そのうち7個(SCRT用に5個、DMA用に2個、メモリからの割り込み用)がすぐに失われました。ああ、現実に対するマーケティングの勝利:-)
System zは実際には非常によく似ており、R14およびR15レジスターを呼び出し/戻りに使用します。
C ++(Cに変換可能)stack.ccの場合:
static int
find_stack_direction ()
{
static char *addr = 0;
auto char dummy;
if (addr == 0)
{
addr = &dummy;
return find_stack_direction ();
}
else
{
return ((&dummy > addr) ? 1 : -1);
}
}
static
このためにを使用する必要はありません。代わりに、アドレスを引数として再帰呼び出しに渡すことができます。
static
、これを複数回呼び出すと、後続の呼び出しが失敗する可能性があります...
MIPSおよび多くの最新のRISCアーキテクチャー(PowerPC、RISC-V、SPARCなど)にはpush
、pop
説明はありません。これらの操作は、スタックポインターを手動で調整してから、調整されたポインターに対して相対的に値をロード/保存することによって明示的に行われます。すべてのレジスター(ゼロ・レジスターを除く)は汎用であるため、理論的にはどのレジスターもスタック・ポインターにすることができ、スタックはプログラマーが望む任意の方向に拡張できます
とは言っても、ほとんどのアーキテクチャでは、スタックとプログラムデータまたはヒープデータが大きくなり、互いに衝突するケースを避けるために、スタックは通常大きくなります。sh-の回答に言及されている大きな対処の理由もあります。いくつかの例:MIPS ABIは下向きに成長し、スタックポインタとして$29
(AKA $sp
)を使用します。RISC -VABIも下向きに成長し、スタックポインタとしてx2を使用します
Intel 8051ではスタックが大きくなります。これはおそらくメモリ領域が非常に小さい(元のバージョンでは128バイト)ためにヒープがなく、ヒープの増加から分離するためにスタックを上に置く必要がないためです。下から
さまざまなアーキテクチャでのスタックの使用に関する詳細については、https://en.wikipedia.org/wiki/Calling_conventionを参照してください。
こちらもご覧ください
他の回答へのほんの少しの追加ですが、私が見る限り、この点には触れていません。
スタックを下向きに成長させると、スタック内のすべてのアドレスがスタックポインタに対して正のオフセットになります。負のオフセットは未使用のスタックスペースのみを指すため、必要ありません。これにより、プロセッサがスタックポインタ相対アドレッシングをサポートしている場合、スタック位置へのアクセスが簡単になります。
多くのプロセッサには、一部のレジスタに対して正のオフセットのみでアクセスを許可する命令があります。それらには、いくつかの古いアーキテクチャだけでなく、多くの近代的なアーキテクチャが含まれます。たとえば、ARM Thumb ABIは、単一の16ビット命令ワード内にエンコードされた正のオフセットを使用したスタックポインタ相対アクセスを提供します。
スタックが上方向に大きくなると、スタックポインタに対するすべての有用なオフセットが負になり、直感的でなく便利ではなくなります。また、たとえば、構造体のフィールドにアクセスするためのレジスタ相対アドレッシングの他のアプリケーションと矛盾しています。
ほとんどのシステムでは、スタックが減少します。https: //gist.github.com/cpq/8598782にある私の記事では、スタックが減少する理由について説明しています。それは簡単です。2つのメモリブロック(ヒープとスタック)を固定メモリチャンクにレイアウトする方法は?最善の解決策は、それらを反対側の端に置き、お互いに向かって成長させることです。