2016-06-21 5 views
2

이 코드를 내 earlier question에서 생각해보십시오.확장 된 asm으로 입력을 전달하는 방법은 무엇입니까?

int main(){ 
    asm("movq $100000000, %rcx;" 
      "startofloop: ; " 
      "sub $0x1, %rcx; " 
      "jne startofloop; "); 
} 

나는 루프는 C 변수의 반복 수를하고 싶다, 그래서 this document을 읽은 후 다음 시도했다.

int main(){          
    int count = 100000000;      
    asm("movq %0, %rcx;"       
      "startofloop: ; "      
      "sub $0x1, %rcx; "     
      "jne startofloop; ":: "r"(count)); 
}             

불행히도, 컴파일에 실패하고 다음 오류로 인해 중단됩니다.

asm_fail.c: In function ‘main’: 
asm_fail.c:3:5: error: invalid 'asm': operand number missing after %-letter 
    asm("movq %0, %rcx;" 
    ^
asm_fail.c:3:5: error: invalid 'asm': operand number missing after %-letter 

C 변수의 값을 어셈블리로 전달하는 올바른 방법은 무엇입니까?

+1

확장 된 어셈블러 템플릿 (입력, 출력, 클로버 등)을 사용하는 경우 레지스터 이름에 여분의 '%'를 추가해야합니다. 어쩌면'%% rcx'을 시도해보십시오.RCX를 덮어 쓰고 출력 피연산자로 나타나지 않으므로 clobber 목록에 나열해야합니다. –

+0

@MichaelPetch, clobber의 좋은 점. 불행히도,'%% rcx'를 할 때, 'movq'에 대해'피연산자 유형 불일치가 발생합니다. – merlin2011

+0

'count'가 32 비트 정수이고 어셈블러 템플릿이 대체를 위해 (eax와 같은) 32 비트 레지스터를 선택했을 가능성이 높습니다. '%'뒤에'q'를 두어 64 비트 레지스터를 사용하려면'% 0'을 사용할 수 있습니다. 'movq % q0, %% rcx;'와 같은 것 –

답변

4

확장 어셈블러 템플릿 (입력, 출력, 클리버 등)을 사용하는 경우 템플리트의 레지스터 이름에 %을 추가해야합니다. 이 경우 . 이렇게하면이 오류와 관련된 문제가 해결됩니다.

error: invalid 'asm': operand number missing after %-letter

이것은 새로운 문제를 나타냅니다.

operand type mismatch for 'movq'

문제는 "r"(count) 입력 제약이 count의 값을 포함하는 레지스터를 선택해야 컴파일러를 알 수 있다는 것입니다 : 당신은 유사한 오류가 발생합니다. count가 int 유형으로 정의되었으므로 32 비트 레지스터를 선택합니다. 논증을 위해서, 그것은 EAX을 선택한다고 가정한다.

movq %eax, %rcx 

당신은 64 비트 레지스터 따라서 오류로 32 비트 레지스터의 내용을 이동 movq을 사용할 수 없습니다 교체 후에는이 명령을 생성하기 위해 노력했을 것이다. 더 나은 선택은 ECX을 대상으로 사용하여 둘 다 동일한 유형이되도록하는 것입니다.

asm("mov %0, %%ecx;"       
    "startofloop: ; "      
    "sub $0x1, %%ecx; "     
    "jne startofloop; ":: "r"(count)); 

는 다른 방법이 "ri"(count)의 입력 피연산자를 사용하도록 선택 할 수 : 같이 개정 코드가 보일 것이다. 이것은 컴파일러가 레지스터 또는 즉시 값을 선택할 수있게합니다. 더 높은 최적화 수준 (-O1, -O2)에 그것은 가능성이 count가 일정하게 유지하는 것이이 경우에 (100,000,000)을 결정하고 생성 코드와 같은 :

mov $100000000, %ecx       
startofloop: 
sub $0x1, %ecx 
jne startofloop 

보다는 레지스터에 100000000를 배치하고에 복사 강제 ECX 대신 즉시 값을 사용할 수 있습니다.


템플릿에서 심각한 문제는 ECX하지만 GCC이에 대한 지식이없는 의 내용을 파괴하는 것입니다. GCC은 코드 내에서 실제로 수행 할 작업을 템플릿에서 구문 분석하지 않습니다. ECX과 구속력을 가지고 있다는 것은 잘 모릅니다. 컴파일러는 템플릿 전후에 동일한 값을 갖는 ECX에 의존 할 수 있습니다. 출력 피연산자에서 참조되지 않은 레지스터를 파괴하면 clobber 목록에 명시 적으로 나열해야합니다.이런 식으로 뭔가가 작동합니다 :

asm("mov %0, %%ecx;" "startofloop: ; " "sub $0x1, %%ecx; " "jne startofloop; ":: "ri"(count) : "rcx"); 

지금 GCC이 전과 템플릿이 실행 된 후 같은 값 인 RCX의 값에 의존 할 수 없다 알고있다.

내부 카운터로 고정 레지스터를 사용하는 대신 GCC을 사용하여 사용할 수있는 것을 선택할 수 있습니다. 이렇게하면 우리는 더 이상 clobber가 필요 없다는 것을 의미합니다. 계산에 사용할 수있는 더미 변수 (임시 변수)를 만들 수 있습니다. 이 코드가 최적화되지 않도록하기 위해 어셈블러 템플릿에 volatile 특성을 사용할 수 있습니다. 어셈블러 템플릿에 출력 피연산자가없는 경우에는 필요하지 않습니다. 다음과 같은 코드가 작동 것이다 :

int count=100000000 
int dummy; 
asm volatile("mov %1, %0;"       
    "startofloop: ; "      
    "sub $0x1, %0; "     
    "jne startofloop; ":"=rm"(dummy): "ri"(count)); 

=rm 출력 제약은 메모리 위치 또는 레지스터 중 하나가이 피연산자에 이용 될 수 있다고 말한다. 컴파일러를 선택하면 더 나은 코드를 생성 할 수 있습니다. 이 경우

mov $0x5f5e100,%ebx 
startofloop: 
sub $0x1,%ebx 
jne startofloop 

컴파일러가 카운트 즉시 피연산자 ($ 0x5f5e100 = $ 100000000)를 사용하기로 결정했습니다 : -O1의 최적화 수준에서 당신은 가능성과 같을 것이다 생성 된 코드를 찾아 낼 것입니다. dummy 변수는 레지스터 EBX까지 최적화되었습니다.

템플릿을 개선하기 위해 할 수있는 다른 트릭이 있습니다. 하나는 변수 count의 값을 보존하기 위해 등장 GNU documentation


코드의 확장 어셈블러 템플릿에 대한 자세한 내용을보실 수 있습니다. 템플릿을 실행하기 전에 count이 동일한 값을 가져야하는 요구 사항이 아니라면 입력 및 출력 모두에 count을 사용할 수 있습니다.

asm volatile("startofloop: ; " 
    "sub $0x1, %0; " 
    "jne startofloop; ":"+rm"(count):); 

+rm

출력 오퍼랜드는 입력 오퍼랜드로서 사용되는 것을 의미처럼 그 코드는 볼 수 있었다. 이 경우 완료되면 count은 항상 0이어야합니다.


당신이 출력에 GCC는-S 옵션을 생성 된 어셈블리 코드를 사용하는 경우 다음 출력이 청소기 보이는 귀하의 템플릿을 변경하실 수 있습니다. ; (세미콜론) 대신 \n\t을 사용하십시오. 이렇게하면 어셈블러 템플릿이 여러 줄로 나뉘어 들여 쓰기가 추가됩니다. 예 : 아무런 대안이없는 경우를 제외하고

일반적으로
asm volatile("mov %1, %0\n\t"       
    "startofloop:\n\t"      
    "sub $0x1, %0\n\t"     
    "jne startofloop\n\t":"=rm"(dummy): "ri"(count)); 

말하기, 당신은 인라인 어셈블러 템플릿을 사용할 수 없습니다. C에 코드를 작성하고 원하는 어셈블러를 출력하도록 컴파일러를 안내하거나 필요한 경우 컴파일러 내장 함수를 사용하십시오. 인라인 어셈블러는 최후의 수단으로 사용되거나 숙제가 요구 될 경우 사용해야합니다. David Wohlferd는 주제에 Wiki article이라고 썼습니다.

+2

우수 논문. 추가 할 수있는 유일한 다른 점은 1) 기호 이름을 사용하여 어셈블러 코드를 읽기 쉽게 만드는 것을 고려해야합니다. 2) 인라인 asm은 재미 있고 흥미롭고 도전적이며 교육적이며 생산 코드에서 나쁜 아이디어입니다. 나는이 사람이 아직 그것을들은 적이 없었기 때문에 항상 그렇게 말합니다. 3) "cc"clobber. 4)'더미 '가 사용되지 않을 가능성이 있으므로, 최적화 프로그램이 전체 코드를 불필요한 코드로 버리지 않도록하는 것이 좋습니다. –

+0

@DavidWohlferd 2에 관해서 : 나는 그 점을 논의하는 또 다른 대답에 링크 할 수 있습니다. 포인트 3에 관해서 : _gcc_는 확장 된 어셈블러 템플릿에서 "cc"가 clobbered라고 더 이상 추측하지 않습니까? 4에 관해서 : 그건 사실입니다, 나는 그 코드가 어떻게 든 최소한의 예라고 생각했습니다. 그가 생성 된 코드를 관찰하기를 원한다면 휘발성 코드를 추가 할 것입니다. ;-) –

+0

2 : 여기에 OP 의도가 교육이라고 분명 합니다만, 이런 식으로 성장하는 것이 쉽습니다. 그렇지 않으면 영감을받을 것입니다. 3 : gcc *는 i386에서 cc를 clobber 않습니다. 그러나 (내 노력에도 불구하고) 이것은 문서화되거나 지원되는 기능이 아니기 때문에 이론적으로는 의존 할 수 없습니다. 현실에서는 변화가없는 것을 볼 수 없으므로 아마 * 안전 할 수도 있지만 추가하는 것은 여전히 ​​훌륭한 자체 문서화입니다. 4 : 음, 'asm volatile'하지 않아야합니까? –

관련 문제