コンパイル時の挙動の違いについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
pocket
記事: 49
登録日時: 8年前

コンパイル時の挙動の違いについて

#1

投稿記事 by pocket » 7年前

お世話になっております.

現在ライブラリ等が一切ない環境でコンパイルをしています.
使用言語はC言語です.
以下のコードをコンパイルして,実行ファイルを作成しようとしたところ,以下のエラーが出ました.

sample.c

コード:

int main(){
int i;
	for(i=0; i<10; i++){
		int j=i/3;
	}
}

int test(int n, int m){
    return (n/m);
}

コード:

arm-none-eabi-gcc -S sample.c
arm-none-eabi-as -o sample.o sample.s
arm-none-eabi-ld -Ttext 0x02000000 -o sample.out sample.o
sample.o: In function `test':
sample.c:(.text+0x78): undefined reference to `__aeabi_idiv'
make: *** [sample.out] Error 1
リンク時にdivの関数(割り算)が無いためにエラーが出ているのだと思います.
今回の環境では標準ライブラリ等もありませんので,基本的な演算(+-*)しかできないのだと思いました.
ですが,外部で定義した関数testではエラーが出ているのに対して,
main関数内で割り算をしている場合にエラーが出ていないのはなぜなのか疑問に思いました.

以前に,コンパイラが割り算はシフト演算子に置き換えるといった話を聞いたことがあります.
しかし,main内はエラーがなく,test関数ではエラーが出ているので,シフト演算子に置き換えるという考えは違うのかなと思いました.

また,コンパイラが数字を置き換えるという話も聞いたことがあります.
例えば,以下のコードを考えます.

コード:

int a=3;
int b=5;
int c=a+b;
このコードはコンパイルが終わる時点で,c=8が代入された状態に最適化されるということです.
今回の問題に当てはめたときに,testでは,n/mとしており,計算して置き換えることができないため,外部ライブラリをリンクしようとしてエラーが出ているのかと思いました.
確かにmainでは,すべてのjの値は計算できますので,for文内全てが最終的な結果であるj=9/3として置き換えられているということでしょうか?
ですが,いくつかのパターンを試してもmain内ではエラーが出ませんでした.(配列に代入してみたりなど)


この疑問に対して,もしお詳しい方がいらっしゃいましたら,ご教授いただければ幸いです.
よろしくお願いいたします.

あんどーなつ
記事: 171
登録日時: 7年前
連絡を取る:

Re: コンパイル時の挙動の違いについて

#2

投稿記事 by あんどーなつ » 7年前

0割りした時の例外処理でしょうか。

これはどうですか?

コード:

int main(){
int i;
volatile int three = 3;
    for(i=0; i<10; i++){
        int j=i/three;
    }
}
 
int test(int n, int m){
    return (n/m);
}

pocket
記事: 49
登録日時: 8年前

Re: コンパイル時の挙動の違いについて

#3

投稿記事 by pocket » 7年前

あんどーなつさん

こちらのスレッドにも返信いただきありがとうございます.

ご指摘いただきました,ソースコードを実行した結果が以下になります.

コード:

arm-none-eabi-gcc -S sample.c
arm-none-eabi-as -o sample.o sample.s
arm-none-eabi-ld -Ttext 0x02000000 -o sample.out sample.o
sample.o: In function `main':
sample.c:(.text+0x2c): undefined reference to `__aeabi_idiv'
sample.o: In function `test':
sample.c:(.text+0x7c): undefined reference to `__aeabi_idiv'
make: *** [sample.out] Error 1
確かにmain関数内でもリンクエラーが出ています.
volatile演算子はコンパイラの最適化を避ける演算子だと思いますが,
「0割りした時の例外処理」とはどういうことでしょうか.

こちらももう少し詳細にご説明いただければ幸いです.
よろしくお願いいたします.

あんどーなつ
記事: 171
登録日時: 7年前
連絡を取る:

Re: コンパイル時の挙動の違いについて

#4

投稿記事 by あんどーなつ » 7年前

うーん、適当に言いすぎました。
aebi_idivのidivで検索すると、IDIVがアセンブラ命令で多くヒットしました。

gccでARM系CPUで動作するプログラムのビルドを試みていると思いますが、
ARM系CPUには、サポートされている命令に随分違いがあります。
それを吸収するようなライブラリがないと怒られているのかもしれません。

あんどーなつ
記事: 171
登録日時: 7年前
連絡を取る:

Re: コンパイル時の挙動の違いについて

#5

投稿記事 by あんどーなつ » 7年前

申し訳ありません、ARM系のgccコンパイルは詳しくないです。
エラーメッセージで検索をかけると、英語のページがヒットすると思います。
そこに参考となるものがあると思いますので、英語が苦でなければそちらのほうをあたってみてください。

pocket
記事: 49
登録日時: 8年前

Re: コンパイル時の挙動の違いについて

#6

投稿記事 by pocket » 7年前

あんどーなつさん

コメントいただきありがとうございます.
今回の環境では最低限のライブラリしかありませんので,リンクエラーは気にしないことにします.
本スレッドの目的はmainではエラーが出ないのに,testではエラーが出る原因の追究です.

あんどーなつさんのアドバイスを参考に再考しましたところ,一応結論が出ました.
sample.cとsample.sの対応を記載します.
※アセンブリの方は読む必要ないです.スキップして下のコメントに飛んでください.

sample1.c

コード:

int main(){

int i;
int three = 3;
    for(i=0; i<10; i++){
        int j=i/three;
    }
}
 
int test(int n, int m){
    return (n/m);
}
sample1.s

コード:

main:
	@ Function supports interworking.
	@ args = 0, pretend = 0, frame = 16
	@ frame_needed = 1, uses_anonymous_args = 0
	stmfd	sp!, {fp, lr}
	add	fp, sp, #4
	sub	sp, sp, #16
	mov	r3, #3
	str	r3, [fp, #-12]
	mov	r3, #0
	str	r3, [fp, #-8]
	b	.L2
.L3:
	ldr	r0, [fp, #-8]
	ldr	r1, [fp, #-12]
	bl	__aeabi_idiv    ←ココ!!
	mov	r3, r0
	str	r3, [fp, #-16]
	ldr	r3, [fp, #-8]
	add	r3, r3, #1
	str	r3, [fp, #-8]
.L2:
	ldr	r3, [fp, #-8]
	cmp	r3, #9
	ble	.L3
	mov	r0, r3
	sub	sp, fp, #4
	@ sp needed
	ldmfd	sp!, {fp, lr}
	bx	lr
	.size	main, .-main
	.align	2
	.global	test
	.type	test, %function
test:
	@ Function supports interworking.
	@ args = 0, pretend = 0, frame = 8
	@ frame_needed = 1, uses_anonymous_args = 0
	stmfd	sp!, {fp, lr}
	add	fp, sp, #4
	sub	sp, sp, #8
	str	r0, [fp, #-8]
	str	r1, [fp, #-12]
	ldr	r0, [fp, #-8]
	ldr	r1, [fp, #-12]
	bl	__aeabi_idiv   ←ココ!!
	mov	r3, r0
	mov	r0, r3
	sub	sp, fp, #4
	@ sp needed
	ldmfd	sp!, {fp, lr}
	bx	lr
	.size	test, .-test
	.ident	"GCC: (devkitARM release 44) 4.9.2"
sample2.c

コード:

int main(){

int i;
int three = 3;
    for(i=0; i<10; i++){
        int j=i/3;
    }
}
 
 
int test(int n, int m){
    return (n/m);
}
sample2.s

コード:

main:
	@ Function supports interworking.
	@ args = 0, pretend = 0, frame = 16
	@ frame_needed = 1, uses_anonymous_args = 0
	@ link register save eliminated.
	str	fp, [sp, #-4]!
	add	fp, sp, #0
	sub	sp, sp, #20
	mov	r3, #3
	str	r3, [fp, #-12]
	mov	r3, #0
	str	r3, [fp, #-8]
	b	.L2
.L3:
	ldr	r3, [fp, #-8]
	ldr	r2, .L4
	smull	r1, r2, r3, r2
	mov	r3, r3, asr #31
	rsb	r3, r3, r2
	str	r3, [fp, #-16]
	ldr	r3, [fp, #-8]
	add	r3, r3, #1
	str	r3, [fp, #-8]
.L2:
	ldr	r3, [fp, #-8]
	cmp	r3, #9
	ble	.L3
	mov	r0, r3
	sub	sp, fp, #0
	@ sp needed
	ldr	fp, [sp], #4
	bx	lr
.L5:
	.align	2
.L4:
	.word	1431655766
	.size	main, .-main
	.global	__aeabi_idiv
	.align	2
	.global	test
	.type	test, %function
test:
	@ Function supports interworking.
	@ args = 0, pretend = 0, frame = 8
	@ frame_needed = 1, uses_anonymous_args = 0
	stmfd	sp!, {fp, lr}
	add	fp, sp, #4
	sub	sp, sp, #8
	str	r0, [fp, #-8]
	str	r1, [fp, #-12]
	ldr	r0, [fp, #-8]
	ldr	r1, [fp, #-12]
	bl	__aeabi_idiv  ←ココ!!
	mov	r3, r0
	mov	r0, r3
	sub	sp, fp, #4
	@ sp needed
	ldmfd	sp!, {fp, lr}
	bx	lr
	.size	test, .-test
	.ident	"GCC: (devkitARM release 44) 4.9.2"
アセンブリ言語をがんばって読みましたところ,以下のコードが問題ということがわかりました.(sample.sにコメントを入れてあります)

コード:

 bl  __aeabi_idiv
結論的には「変数/変数」をした場合に,コンパイラが「 bl __aeabi_idiv」の命令を作り出すようです.
「変数/数字」の場合は,「 bl __aeabi_idiv」の命令は作らず,コンパイラは別の方法で解決するようです.

あんどーなつさんのコードのvalatileを消しても,mainでエラーが出ました.
コンパイルオプションで最適化を無効にしてみましたが,効果がなかったので,
今回は最適化はあまりかんけいなかったようです.

以上です.
この疑問に対してお時間を割いていただいた方々に感謝いたします.
本当にありがとうございました.

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: コンパイル時の挙動の違いについて

#7

投稿記事 by ISLe » 7年前

ARMプロセッサの、整数によるゼロ除算に関する技術文書
http://infocenter.arm.com/help/index.js ... DDJEA.html

閉鎖

“C言語何でも質問掲示板” へ戻る