2011-12-12 4 views
9

GCC의 인라인 어셈블러를 사용하여 x86 어셈블리에 익숙해 지려고합니다. 두 개의 숫자 (ab)를 추가하고 결과를 c에 저장하려고합니다. 저는 약간 다른 시도가 4 번 있는데 그 중 3 가지가 효과가 있습니다. 마지막은 예상 된 결과를 생성하지 않습니다.두 개의 숫자 추가하기

처음 두 예제는 중간 레지스터를 사용하며 두 함수 모두 정상적으로 작동합니다. 세 번째와 네 번째 예제는 두 개의 값을 중간 레지스터없이 직접 추가하려고 시도하지만 결과는 최적화 수준과 입력 값을 추가하는 순서에 따라 달라집니다. 나는 무엇이 잘못 되었는가?

환경이다

int a = 4; 
int b = 7; 
int c; 

예 1 :

asm(" movl %1,%%eax;" 
    " addl %2,%%eax;" 
    " movl %%eax,%0;" 
    : "=r" (c) 
    : "r" (a), "r" (b) 
    : "%eax" 
    ); 
printf("a=%d, b=%d, c=%d\n", a, b, c); 
// output: a=4, b=7, c=11 

예 2

다음

i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3) 

우선, 변수 선언

asm(" movl %2,%%eax;" 
    " addl %1,%%eax;" 
    " movl %%eax,%0;" 
    : "=r" (c) 
    : "r" (a), "r" (b) 
    : "%eax" 
    ); 
printf("a=%d, b=%d, c=%d\n", a, b, c); 
// output: a=4, b=7, c=11 

예 3

asm(" movl %2,%0;" 
    " addl %1,%0;" 
    : "=r" (c) 
    : "r" (a), "r" (b) 
    ); 
printf("a=%d, b=%d, c=%d\n", a, b, c); 
// output with -O0: a=4, b=7, c=11 
// output with -O3: a=4, b=7, c=14 

예 4

// this one appears to calculate a+a instead of a+b 
asm(" movl %1,%0;" 
    " addl %2,%0;" 
    : "=r" (c) 
    : "r" (a), "r" (b) 
    ); 
printf("a=%d, b=%d, c=%d\n", a, b, c); 
// output with -O0: a=4, b=7, c=8 
// output with -O3: a=4, b=7, c=11 

을 해결했다.Matthew Slattery's answer이 맞습니다. 전에 bc 모두 eax을 재사용하려고했던 : 장소에 매튜의 제안 수정 프로그램

movl -4(%rbp), %edx 
movl -8(%rbp), %eax 
movl %edx, %eax 
addl %eax, %eax 

는, 지금 별도로 c를 개최 ecx를 사용합니다.

movl -4(%rbp), %edx 
movl -8(%rbp), %eax 
movl %edx, %ecx 
addl %eax, %ecx 
+2

최적화 사용 여부에 관계없이 정상적으로 작동합니다. 어셈블리 언어 목록을 얻으려면 -S로 컴파일 해보십시오. 그러면 어떤 레지스터가 사용 중인지 알 수 있습니다. – TonyK

+0

최적화 수준에 따라 다른 결과가 나타났습니다. 코드 샘플이 새로운 출력으로 업데이트되었습니다. –

+0

그럼 조립 목록에서 무엇을 말합니까? – TonyK

답변

7

기본적으로 gcc은 인라인 asm 블록이 출력 피연산자를 업데이트하기 전에 입력 피연산자를 사용한다고 가정합니다. 이것은 입력과 출력이 동일한 레지스터에 지정 될 수 있음을 의미합니다.

그러나, 그것은 반드시 예를 들어, 귀하의 예 3의 경우, 4

아니다 예 3 :

asm(" movl %2,%0;" 
    " addl %1,%0;" 
    : "=r" (c) 
    : "r" (a), "r" (b) 
    ); 

... 당신이 a (%1)을 읽기 전에 c (%0) 업데이트되었습니다. gcc 모두 %0%1에 같은 레지스터를 할당 발생하면, 그것은 c = b; c += c을 계산합니다, 따라서 당신이 관찰 정확히 방법으로 실패합니다 gcc을 말함으로써

printf("a=%d, b=%d, c=%d\n", a, b, c); 
// output with -O0: a=4, b=7, c=11 
// output with -O3: a=4, b=7, c=14 

당신은 그것을 해결할 수 출력 피연산자가 수

asm(" movl %2,%0;" 
    " addl %1,%0;" 
    : "=&r" (c) 
    : "r" (a), "r" (b) 
    ); 

합니다 (gcc 문서에 "Constraint Modifier Characters" 참조).

,617,451 : 입력이 소모되기 전에 같이 피연산자에 " &"개질제를 첨가하여 사용
+0

그게 다예요, 고마워요. 수정 전후의 어셈블리 목록으로 질문을 업데이트했습니다. –

+0

감사합니다. 그것은 내가 이해할 필요가있는 것이지만, 문서는 오히려 금지되어 있습니다. 이와 같은 팁은 매우 가치가 있습니다. – TonyK

0

호이, 거기에는 문제가 보이지 않으며 컴파일되어 잘 작동합니다. 그러나, 작은 힌트 : 나는 아주 빨리 이름없는 변수/레지스터와 혼동하여 명명 된 것들을 사용하기로 결정했다. 추가 기능은 예를 들어 다음과 같이 구현할 수 말았 :

static inline void atomicAdd32(volInt32 *dest, int32_t source) { 
// IMPLEMENTS: add m32, r32 
__asm__ __volatile__(
     "lock; addl %[in], %[out]" 
     : [out] "+m"(*dest) 
     : [in] "ir"(source)//, "[out]" "m"(*dest) 
     ); 
return; 
    } 

(당신이 지금의 원자/잠금 것들을 무시할 수), 즉하게 분명 무슨 일 :

1) 읽기, 쓰기 무엇 레지스터 또는 둘 다

2) 메모리와 액세스하는 것보다 레지스터 조작이 빠르기 때문에 성능과 클럭 사이클에 중요한 2) 사용되는 (메모리, 레지스터).

건배, G.

P.S : 당신이 당신의 컴파일러가 코드를 재 배열 여부를 확인해 봤어?

관련 문제