티스토리 뷰


편견이 깨지는 어셈블리 프로그래밍 - 최적화 강좌 1 - 4  
 
 





어셈블리
앞 에서도 이야기했지만 고급 언어들의 컴파일러들은 많은 노력과 기술의 발전으로 상당한 최적화가 이뤄졌다. 하지만 CPU에 특성화되지 않는 범용 언어이기 때문에 최적화에는 한계가 있다. 이러한 부족함을 보안하기 위해 우리는 어셈블리(assembly)의 필요성을 느끼게 된다. 하지만 어셈블리로 프로그램 하나를 전체적으로 만든다는 것은 어렵고 시간이 걸리는 일이다. 이에 우리는 새로워진 매크로 어셈블리와 여러 가지 기법을 통해 이에 대한 해결책을 제시하고자 한다.

매크로 어셈블러
여러 다른 언어들이 그동안 계속 발전했듯이 어셈블리도 계속 발전해 왔다. 그 중에서 필자는 매크로 어셈블러(macro assembler)를 소개할까 한다. 매크로 어셈블러는 마이크로소프트에서 제작한 것으로 모듈화를 위한 매크로(macro) 기능이 돋보이는 어셈블러이다. 매크로 어셈블러를 대표하는 MASM 5.0에서 MASM 6.0 세대로 넘어오면서(다른 고급 언어에 비해 어셈블러는 버전 업이 굉장히 느리다) 프로그래밍 작업을 편리하게 만드는 여러 기능을 추가했는데 그 중에서 하나가 고급 명령 지원이다.
한 가지 알아 둘 것은 이 고급 문법은 CPU에서 지원해주는 명령어는 아니다. 즉 컴파일시 하나의 Opcode로 변환되는 것이 아니라 몇 개의 기존의 명령어 조합으로 바뀐다. 바꿔 말하면 컴파일러에서 사용자 편의를 위해 지원해 주는 매크로인 것이다. 그러면 코드를 통해서 MASM 6.0 이상의 버전은 그 이전 버전의 MASM과 어떻게 다르며 무엇이 쉬워졌는지 알아보자

프로시저 - invoke, proto, proc
어셈블러에서는 타언어의 함수 호출을 할 수 있게 일일이 함수 타입을 맞춰 스택에 파라미터들을 Push해주곤 해야 했다. 하지만 MASM이 6.0대에 들어서면서 이런 불편들을 해소하기 위해 PROC 명령어를 확장하고 INVOKE와 PROTO를 추가함으로써 ONE PASS 컴파일러의 함수 호출의 면모를 보여 주기 시작했다. 그럼 간단하게 예를 들어 설명해보자(<리스트 1, 2>).

<리스트 1> MASM5.0에서 작성한 stack과 call명령어를 사용한 함수 작성
.586
.model flat, stdcall
option casemap:none

.data
V_Temp dd (0)

.code
start:

Main proc

push 3
push 2
push 1
call Fun_AddThreeValue
;invoke Fun_AddThreeValue, 1, 2, 3
mov V_Temp,eax

Main endp

;-------------------------------------------------------;
; ;
; 함수 이름 : Fun_AddThreeValue ;
; 동 작 : 3개의 dword값을 받아 들여서 더한다 ;
; 반 환 값 : 더한값 ;
; ;
;-------------------------------------------------------;
Fun_AddThreeValue proc
push ebp
mov ebp,esp
push ebx

mov ebx, dword ptr [ebp+4]
add ebx, dword ptr [ebp+8]
add ebx, dword ptr [ebp+12]
mov eax, ebx

push ebx
mov esp,ebp
pop ebp

ret

Fun_AddThreeValue endp

end start

<리스트 2> MASM7.0에서 작성한 Invoke를 사용한 함수 작성
.586
.model flat, stdcall
option casemap:none

Main proto
Fun_AddThreeValue proto A_ValueOne:dword, A_ValueTwo:dword, A_ValueThree:dword

.data
V_Temp dd (0)

.code
start:

Main proc

................생략 ...........

push 3
push 2
push 1
call Fun_AddThreeValue (00401040)

................생략 ...........

Main endp

;-------------------------------------------------------;
; ;
; 함수 이름 : Fun_AddThreeValue ;
; 동 작 : 3개의 dword값을 받아 들여서 더한다 ;
; 반 환 값 : 더한값 ;
; ;
;-------------------------------------------------------;

Fun_AddThreeValue proc A_ValueOne:dword, A_ValueTwo:dword, A_ValueThree:dword
push ebx

mov ebx, A_ValueOne
add ebx, A_ValueTwo
add ebx, A_ValueThree
mov eax, ebx

push ebx

ret

Fun_AddThreeValue endp

end start

함 수를 구성할 때 ‘Invoke, Proto, Proc’ 명령어를 사용함으로써 문법이 단순해지는 것을 볼 수 있다. 몇 가지 특징을 볼 수 있는데 우선 함수를 사용하려면 헤더를 선언해야 되는데, 이 함수의 프로토 타입을 선언해 주는 명령이 ‘proto’이다. 여기서 보면 proc와 연계해 사용하는데 proc와 연계되지 않고 proto 타입만 선언되어 있을 경우 컴파일러는 자동적으로 외부 함수 호출로 받아들인다. 고급 언어의 함수 선언과 다를 바 없지 않은가?
여기서 invoke와 proto를 사용한 함수 선언 및 지정은 아규먼트 전달에 스택을 사용하기 때문에 몇몇 어셈블리를 사용하는 독자는 속도 향상 없이 어셈블리답지 않고 어셈블리의 맛이 없다고 얘기하는 경우도 있을 것이다. 하지만 우리가 만약 한 클럭이라도 아끼고자 코딩해야 되는 상황이라면 당연히 그래야겠지만 기본적으로 함수 호출과 같이 어쩔 수 없이 브랜치가 발생하는 상황에서 파라미터 설정 정도의 클럭을 아끼는 것보다 대량의 연산 등과 같은 부분에서의 최적화가 훨씬 도움이 된다는 것을 생각하면 앞으로 코딩을 하면서 무엇을 버리고 무엇을 남겨 두는 것이 프로젝트의 완성도를 높여 주는 것인지를 생각하는데 도움이 될 것이다. 즉, 클럭을 아끼는 것도 중요하겠지만 큰 흐름을 생각한다면 충분히 접어 둘 수 있는 사항인 것 같다.


출처: http://www.imaso.co.kr/?doc=bbs/gnuboard.php&bo_table=article&wr_id=75