2016-09-18 11 views
0

을 가끔 뭔가의 배열을 반복하는이 패턴을 사용제대로 조건을 테스트하는 방법 : JE 또는 jge

mov [rsp+.r12], r12 ; Choose a register that calls inside the loop won't modify 
    mov r12, -1 
.i: 
    inc r12 
    cmp r12, [rbp-.array_size] 
    je .end_i 
    ; ... program logic ... 
    jmp .i 
.end_i: 
    mov r12, [rsp+.r12] 

나는 평등을 테스트하기에 충분하지만, 이해해야한다 "에 대한없는 일"안전하게 "테스트 보다 크거나 같음 "(발생하지 않을 상황을 방지하십시오).

이 경우 je 또는 jge를 사용해야합니까?

버그를 도입 할 가능성을 줄일 수있는 구체적인 팁을 묻습니다.

+0

저는 평상시가 아닌 우연히 범위를 테스트하는 아이디어가 좋았습니다. 실수로 비트가 넘칠 경우를 대비해서 말이죠. 그러나 x86 asm에서,'cmp/jge'는 Core2 (32 비트 모드)에서 매크로 퓨즈 할 수 없지만'cmp/je'는 할 수 있다는 것을 명심하십시오. 나는 Core2에서 64 비트 모드로 매크로 융합이 전혀 작동하지 않기 때문에 그것이 확인 될 때까지 더 관련성이 높을 것이라고 생각했고, 그것이 Nehalem이 아니라 Core2만이 융합 될 수 있다는 것을 발견했습니다. (나중에 마이크로 아키텍처는 그 한계가 없기 때문에 더 많은 조합을 매크로 퓨즈 할 수 있습니다.) –

+0

왜 임시 카운터로 사용하기 위해 r12의 이상한 유출/재로드를 보여 줍니까? 그건 전혀 상관 없습니다 (효율적인 코드처럼 보이지는 않습니다). 이미 저장하지 않고 사용할 수있는 이미 등록 된 레지스터가 있습니다. –

+0

@ 피터, 어떻게 제대로 작성해야하나요? printf와 같은 루프 내부의 함수 호출은 호출 저장 r12 레지스터를 수정하지 않으므로 r12를 사용하는 것이 좋습니다. 따라서 호출을 수동으로 저장하고 호출을 복원 할 필요가 없습니다.잘못 되었다면 수정하십시오. –

답변

3

저는 항상 비트가 우연히 또는 뭔가 잘못 대치 할 경우에 대비하여 범위를 테스트하는 아이디어를 좋아했습니다. 그러나 x86 asm에서 cmp/jge은 Core2 (32 비트 모드)에서는 macro-fuse이 아니지만 cmp/je 수 있습니다. 내가 Agner Fog's microarch pdf을 확인하고 Nehalem이 아니라 Core2에서만 64 비트 모드에서 매크로 융합이 전혀 작동하지 않기 때문에 융합 할 수없는 Core2만이 아니라는 것을 발견 할 때까지는 더 많은 관련성이 있다고 생각했습니다. (나중에 마이크로 아키텍처는 그 한계가 없기 때문에 더 많은 조합을 매크로 융합 할 수 있습니다.)

카운터에 따라 CMP없이 전혀 카운트 다운 할 수 없습니다 (dec/jnz). 그리고 종종 64 비트 일 필요가 없다는 것을 알고 있으므로 dec esi/jnz 또는 무엇이든 사용할 수 있습니다. dec esi/jge은 부호있는 카운터에서 작동하지만 dec은 CF를 설정하지 않으므로 JA를 (유용하게) 사용할 수 없습니다.

중간에 if() break이고 마지막에 jmp가있는 루프 구조는 asm에 관용적이지 않습니다. 보통은 다음과 같습니다

mov ecx, 100 

.loop:    ; do{ 
    ;; stuff 
    dec ecx 
    jge .loop  ; }while(--ecx >= 0) 

에만 100..1 대신 100..0 긍정적 인 ECX, 즉 루프와 루프를 다시 시작 JG를 ​​사용할 수 있습니다.

취하지 않은 조건부 분기 을 사용하면 루프에서 무조건 분기를 수행하는 것이 효율적이지 않습니다.


R12를 저장/복원에 대한 질문에 의견을 토론을에 확장 : 일반적으로 당신과 같이 할 것 : 우리가 함수 내에서 몇 가지에 대한 RBX을 사용하는 방법

my_func: 
    ; push rbp 
    ; mov rbp, rsp  ; optional: make a stack frame 

    push rbx   ; save the caller's value so we can use it 
    sub rsp, 32  ; reserve some space 

    imul edi, esi, 11 ; calculate something that we want to pass as an arg to foo 
    mov ebx, edi  ; and save it in ebx 
    call foo 
    add eax, ebx  ; and use value. If we don't need the value in rbx anymore, we can use the register for something else later. 

    ... ;; calculate an array size in ecx 

    test ecx, ecx    ; test for the special case of zero iterations *outside* the loop, instead of adding stuff inside. We can skip some of the loop setup/cleanup as well. 
    jz .skip_the_loop 

    ; now use rbx as a loop counter 
    mov ebx, ecx 
.loop: 
    lea edi, [rbx + rbx*4 + 10] 
    call bar      ; bar(5*ebx+10); 
    ; do something with the return value? In real code, you would usually want at least one more call-preserved register, but let's keep the example simple 
    dec ebx 
    jnz .loop 
.skip_the_loop: 

    add rsp, 32   ; epilogue 
    pop rbx 

    ;pop rbp    ; pointless to use LEAVE; rsp had to already be pointing to the right place for POP RBX 
    ret 

공지 사항, 한 번만 저장/복원 할 수 있습니다.

+1

꽤 많이 생각합니다. 테스트는 일반적으로 루프 끝에서 여분의 JMP를 절약하기 위해 제공되며 매크로 융합과 안전성 때문에 서명 된 비교를 피합니다. 일반적인 실수는 음수 입력을 테스트하는 것을 잊는 것입니다. 부호없는 비교에는이 문제가 없습니다. – icecreamsword

+1

나는 또한 내가 가장 편리한 점에 따라 다양한 스타일로 루프를 작성한다고 덧붙였다. 0에서 N-1까지 (INC/CMP/JB 또는 INC/CMP/JBE); N에서 1 (DEC/JNZ); N-1에서 -1까지 (INC/JNC); 인덱스보다는 포인터를 사용하여 위의 변형이 가능합니다. – icecreamsword

+1

@icecreamsword : asm에서의 프로그래밍이 일반적으로 C보다 signedness와 너비 함수의 너비를 더 많이 선택하는 것이 재미있는가요? C에서는 어떤 사람들은 모든 것을 배열 관련'size_t '로 만듭니다. –