C언어 volatile keyword

C언어에서 volatile을 변수에 적용하면 컴파일러가 최적화를 하지 않고 항상 해당 메모리에 접근한다. volatile의 사전적인 의미는 "변할 수 있는" 정도의 뜻이며, 변할 수 있는 값이기 때문에 컴파일러가 최적화를 할 수 없다. volatile은 HW 접근 시에 보통 사용한다. 아래의 예제를 보자.

* target CPU는 RISC-V이며 컴파일 조건은 아래와 같다.
포스팅 ▶ 테스트 조건 및 성능 / 메모리 사용량 비교 방법
* RISC-V 명령어 설명은 아래를 참조하자.
포스팅 ▶ RISC-V 어셈블리 명령어 설명


<C 코드>

#define HW_QUEUE_POP    (*(int*)0x20000000)

int first_value;
int second_value;

void get_two_values(void)
{
    first_value = HW_QUEUE_POP;
    second_value = HW_QUEUE_POP;
}

<어셈블리 코드>

00000000 <get_two_values>:
   0:	200007b7          	lui	a5,0x20000
   4:	0007a783          	lw	a5,0(a5) # a5 = 20000000번지의 값
   8:	10000737          	lui	a4,0x10000
   c:	00f72223          	sw	a5,4(a4) # 10000004번지(first_value) = a5
  10:	10000737          	lui	a4,0x10000
  14:	00f72023          	sw	a5,0(a4) # 10000000번지(second_vaule) = a5
  18:	00008067          	ret

Disassembly of section .sbss:

10000000 <second_value>:
10000000:	0000                	unimp
	...

10000004 <first_value>:
10000004:	0000                	unimp

위의 예제에서 0x2000_0000번지는 read 하면 HW QUEUE에서 값이 하나씩 pop 되는 HW address이다.

프로그램을 작성한 사람은 0x2000_0000번지 read 한 값이 first_value에 대입되고, 다시 0x2000_0000번지 read 한 값이 second_value에 대입되길 기대했을 것이다. 하지만 어셈블리를 보면 0x2000_0000번지를 한 번만 read 해서 그 값을 first_value, second_value에 똑같이 대입했다. 컴파일러는 같은 메모리 위치의 값이니 여러 번 read 해도 같은 값일 것으로 생각하여 한 번만 read하고 같은 값을 first_value, second_value에 대입했다.

이런 경우에 volatile keyword가 필요하다. 아래에는 HW_QUEUE_POP define에 volatile을 적용했다.


<C 코드>

#define HW_QUEUE_POP    (*(volatile int*)0x20000000)

int first_value;
int second_value;

void get_two_values(void)
{
    first_value = HW_QUEUE_POP;
    second_value = HW_QUEUE_POP;
}

<어셈블리 코드>

00000000 <get_two_values>:
   0:	200007b7          	lui	a5,0x20000
   4:	0007a683          	lw	a3,0(a5) # a3 = 20000000번지의 값
   8:	10000737          	lui	a4,0x10000
   c:	00d72223          	sw	a3,4(a4) # 10000004번지(first_value) = a3
  10:	0007a703          	lw	a4,0(a5) # a4 = 20000000번지의 값
  14:	100007b7          	lui	a5,0x10000
  18:	00e7a023          	sw	a4,0(a5) # 10000000번지(second_value) = a4
  1c:	00008067          	ret

Disassembly of section .sbss:

10000000 <second_value>:
10000000:	0000                	unimp
	...

10000004 <first_value>:
10000004:	0000                	unimp

이번에는 0x2000_0000번지에서 read 해서 fisrt_value에 대입하고 다시 0x2000_0000번지에서 read 해서 second_value에 대입했다. 컴파일러가 volatile이 있어서 최적화를 하지 않고 매번 메모리에서 값을 읽었다.

위와 같이 컴파일러 최적화가 되지 않아야 하는 변수에는 volatile keyword를 사용해야 한다.

* Feedback은 언제나 환영합니다.

Comments