2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cコンパイラ 想定外の最適化とvolatileの役割

Posted at

初めに

running変数にvolatileをつけた場合とつけない場合で比較する

比較するC言語ソース

volatile無し
test1.s
#include <stdio.h>
#include <signal.h>

int running = 1;  // ★ここを変更して比較

// Ctrl+Cハンドラ
void handler(int sig) {
    running = 0;
    printf("Handler called!\n");
}

int main() {
    signal(SIGINT, handler);  // 修正箇所
    while (running); // 最適化で無限ループに

    printf("Exited gracefully\n");
    return 0;
}
volatile有り
test2.s
#include <stdio.h>
#include <signal.h>

volatile int running = 1;  // ★ここを変更して比較

// Ctrl+Cハンドラ
void handler(int sig) {
    running = 0;
    printf("Handler called!\n");
}

int main() {
    signal(SIGINT, handler);  // 修正箇所
    while (running); // 最適化で無限ループに

    printf("Exited gracefully\n");
    return 0;
}

アセンブリ

gcc -S -O2 -masm=intel test1.c -o test1.s
gcc -S -O2 -masm=intel test2.c -o test2.s
volatile無し
test1.s
	.file	"test1.c"
	.intel_syntax noprefix
	.section .rdata,"dr"
LC0:
	.ascii "Handler called!\0"
	.text
	.p2align 4,,15
	.globl	_handler
	.def	_handler;	.scl	2;	.type	32;	.endef
_handler:
LFB12:
	.cfi_startproc
	mov	DWORD PTR _running, 0
	mov	DWORD PTR [esp+4], OFFSET FLAT:LC0
	jmp	_puts
	.cfi_endproc
LFE12:
	.def	___main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
LC1:
	.ascii "Exited gracefully\0"
	.section	.text.startup,"x"
	.p2align 4,,15
	.globl	_main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
LFB13:
	.cfi_startproc
	push	ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	mov	ebp, esp
	.cfi_def_cfa_register 5
	and	esp, -16
	sub	esp, 16
	call	___main
	mov	DWORD PTR [esp+4], OFFSET FLAT:_handler
	mov	DWORD PTR [esp], 2
	call	_signal
	mov	eax, DWORD PTR _running
	.p2align 4,,10
L3:
	test	eax, eax
	jne	L3
	mov	DWORD PTR [esp], OFFSET FLAT:LC1
	call	_puts
	xor	eax, eax
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE13:
	.globl	_running
	.data
	.align 4
_running:
	.long	1
	.ident	"GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
	.def	_puts;	.scl	2;	.type	32;	.endef
	.def	_signal;	.scl	2;	.type	32;	.endef
volatile有り
test2.s
	.file	"test2.c"
	.intel_syntax noprefix
	.section .rdata,"dr"
LC0:
	.ascii "Handler called!\0"
	.text
	.p2align 4,,15
	.globl	_handler
	.def	_handler;	.scl	2;	.type	32;	.endef
_handler:
LFB12:
	.cfi_startproc
	mov	DWORD PTR _running, 0
	mov	DWORD PTR [esp+4], OFFSET FLAT:LC0
	jmp	_puts
	.cfi_endproc
LFE12:
	.def	___main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
LC1:
	.ascii "Exited gracefully\0"
	.section	.text.startup,"x"
	.p2align 4,,15
	.globl	_main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
LFB13:
	.cfi_startproc
	push	ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	mov	ebp, esp
	.cfi_def_cfa_register 5
	and	esp, -16
	sub	esp, 16
	call	___main
	mov	DWORD PTR [esp+4], OFFSET FLAT:_handler
	mov	DWORD PTR [esp], 2
	call	_signal
	.p2align 4,,10
L3:
	mov	eax, DWORD PTR _running
	test	eax, eax
	jne	L3
	mov	DWORD PTR [esp], OFFSET FLAT:LC1
	call	_puts
	xor	eax, eax
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE13:
	.globl	_running
	.data
	.align 4
_running:
	.long	1
	.ident	"GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
	.def	_puts;	.scl	2;	.type	32;	.endef
	.def	_signal;	.scl	2;	.type	32;	.endef

比較

動作

Ctrl+cを押した後の画面

volatile無し

無限ループを抜けない
image.png

volatile有り

無限ループを抜ける
image.png

分析

アセンブリ差分の比較

image.png

volatile無し

一度eaxに入れた1を使い廻しているため、runningに0が入っても無限ループから抜けることができない

無限ループ.s
	mov	eax, DWORD PTR _running
	.p2align 4,,10
L3:
	test	eax, eax
	jne	L3

volatile有り

毎回メモリからrunningの値を読み込んでいる。

無限ループ.s
	.p2align 4,,10
L3:
	mov	eax, DWORD PTR _running
	test	eax, eax
	jne	L3

※test eax, eaxはeaxが0かどうかを調べている
 eaxが0の場合はゼロフラグZF​​が1となる。
その後jne L3にてZFが0でない場合はループを続ける。

メモリの読み込み回数が最適化で減らされ不具合が起きてしまった。(敢えて起こした)

動作環境

windows7 Version6.1.7601
GCC-6.3.0-1
(帰省中につき一時的に昔のパソコンを使用している。windows7が懐かしい。。。)

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?