[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[b-free: 1091] Re: OS による言語へのスタック機構のサポート





隆一です。


From: 片桐 明 <killy@rigy.co.jp>
Subject: [b-free: 1089] Re: OS による言語へのスタック機構のサポート
Date: Wed, 25 Feb 1998 19:30:06 +0900
Message-ID: <199802251029.TAA06763@mb.rigy.co.jp>

>  隆一さん、こんにちは。リギーコーポレーションの片桐です。
> 

こんにちは、片桐さん。


... [snip] ...

> 
>  了解です。
>  register指定がミソですね。これって「運が良ければ」という性質のもの
> と理解しているのですが、確実にレジスタに割り当てるという方策はあるの
> でしょうか。(Cの中身のことはよく知らないものですから)

GCC の場合だと asm という予約語を使えば、C の関数の中に直接アセンブラ
を書けると思います。
GCC の場合、asm の中はそのままアセンブラに落ちるのではなく、C コンパイ
ラが asm 文の中を解析してくれます。たとえば、局所変数をオペランドに直
接書く、なんてことができます。そのとき、局所変数に対して、アセンブラの
オペランドに汎用レジスタを使え、という指定をすると、C コンパイラが前後
の C 言語で書いた部分と合うように自動的に適当なレジスタに割り当ててく
れます。(レジスタを使うように指定することを「制約」というらしいです)


簡単な例を次に示します。

int	bar;

main ()
{
  int	foo;

  asm ("movl $1, %0" : "=r"(foo));
  asm ("movl $2, %0" : "=r"(bar));
  asm ("add %0, %1" : "=r"(foo) , "=r"(bar));		/* bar = foo + bar */
  printf("foo = %d, bar = %d\n", foo, bar);  
}


というプログラムをコンパイルすると、次のアセンブラプログラムになります。 
#APP から #NOAPP で囲まれた部分が asm で指定した部分になります。
これを見ると、変数 foo と bar がそれぞれレジスタ edx と ecx に(コンパ
イラによって自動的に) 割り付けられたことがわかると思います。
このアセンブラは、最適化なしの指定でコンパイルしたので、かなり非効率に
なっています。
たとえば、局所変数 foo は、スタック上に領域をもっているので、edx に 1 
を代入した後にわざわざスタック上の領域に入れています。
最適化ありを指定すると、はじめから、レジスタだけを使うようになります。
(ただ、意味的に間違ったコードが出る??)


	.file	"foo.c"
	.version	"01.01"
gcc2_compiled.:
.section	.rodata
.LC0:
	.string	"foo = %d, bar = %d\n"
.text
	.align 4
.globl main
	.type	 main,@function
main:
	pushl %ebp
	movl %esp,%ebp
	subl $4,%esp
#APP
	movl $1, %edx		<-- 変数 foo を %edx にわりつけ、定数 
#NO_APP				    1 を代入する。
	movl %edx,-4(%ebp)	<-- さらにわざわざスタック上の領域にも
#APP				    値をコピーしている。
	movl $2, %ecx		<-- 変数 bar を %ecx にわりつけ、定数 
#NO_APP				    2 を代入する。
	movl %ecx,bar		<-- 大域変数 bar の領域にも入れる。
#APP
	add %edx, %ecx		<-- foo と bar の値を加算。
#NO_APP
	movl %edx,-4(%ebp)
	movl %ecx,bar
	movl bar,%eax
	pushl %eax
	movl -4(%ebp),%eax
	pushl %eax
	pushl $.LC0
	call printf
	addl $12,%esp
.L1:
	leave
	ret
.Lfe1:
	.size	 main,.Lfe1-main
	.comm	bar,4,4
	.ident	"GCC: (GNU) 2.7.2.3"



最適化あり、の場合:

	.file	"foo.c"
	.version	"01.01"
gcc2_compiled.:
.section	.rodata
.LC0:
	.string	"foo = %d, bar = %d\n"
.text
	.align 4
.globl main
	.type	 main,@function
main:
	pushl %ebp
	movl %esp,%ebp
#APP
	add %eax, %edx		<-- はじめからレジスタだけを使っている
				    でも、初期化していない。
#NO_APP
	movl %edx,bar
	pushl %edx
	pushl %eax
	pushl $.LC0
	call printf
	leave
	ret
.Lfe1:
	.size	 main,.Lfe1-main
	.comm	bar,4,4
	.ident	"GCC: (GNU) 2.7.2.3"



> |もちろん、特権レベルが高くないと使えないようなシステムレジスタ(セグメ
> |ントレジスタなど)は、ユーザプログラムでは使えません。しかし、システム
> |レジスタの類は、ユーザプログラムレベルで普通のプログラムを組む場合には
> |必要ないと思います。
> 
>  はい、単純に間接メモリアクセスができるだけで良いです。
>  ・・あ、そうか。
>  かなり都合の良いregister変数が出来たとすると、
> 
>        *--p = data;
> 
>  が機械語のPUSHと等価になるんですね。

ということで、GCC だと機械語の PUSH を asm で書けます。
ただ、強制的に局所変数をレジスタを使うようにしても、前後の C の部分に
よっては、かなり非効率なコードになってしまいそうです。


それから、今 Linux のソース(そう、Linux のカーネルのソースは、asm の使
いかたを調べるには格好の教材です)を見たら、

  register unsigned long foo asm("ax");

というような局所変数の宣言もできるみたいです。
(これは、局所変数 foo を レジスタ eax に割りつけるという意味です)



> |この辺の話は、Windows でも同じかと思っていたんですが、ひょっとして、
> |Windows ではレジスタの使いかたが違うんでしょうか?
> 
>  すみません、こちらも知識ありません(^^;。Pentiumでレジスタ数は多少
> 多くなっているんでしたっけ。
> 

Pentium でも汎用レジスタの数は変わりないと思いましたが。。。


PS.
  ちなみに、B-Free OS では、asm は使っていません。
  (アセンブラが必要な部分は、ファイルを分けています)

	

---
B-Free プロジェクト実行中! 詳細はこの Web ページへ
(http://www.sccs.chukyo-u.ac.jp/B-Free) 


内藤隆一 (rnaitoh@st.rim.or.jp)