programing

asm, _asm, _asm_의 차이점은 무엇입니까?

prostudy 2022. 7. 21. 23:48
반응형

asm, _asm, _asm_의 차이점은 무엇입니까?

내가 아는 한, 두 사람의 유일한 차이점은__asm { ... }; ★★★★★★★★★★★★★★★★★」__asm__("..."); 번째가 '네'를 사용한다는 입니다.mov eax, var 두 번째는 '''를 사용합니다.movl %0, %%eax:"=r" (var)그밖 어떤 ?이 이? ??? 그냥...asm

MSVC 인라인 asm과 GNU C 인라인 asm 사이에는 큰 차이가 있습니다.GCC 구문은 하나의 명령어를 랩핑하거나 하는 불필요한 명령어를 사용하지 않고 최적의 출력을 제공하도록 설계되어 있습니다.MSVC 구문은 매우 심플하게 설계되어 있지만, AFAICT는 입력과 출력에 대한 메모리 왕복의 레이텐시와 추가 명령 없이는 사용할 수 없습니다.

퍼포먼스상의 이유로 인라인 asm을 사용하고 있는 경우는, MSVC 인라인 asm이 유효하게 되는 것은, 짧은 시퀀스를 인라인 함수로 래핑 하는 것이 아니고, 루프 전체를 asm으로 쓰는 경우 뿐입니다.)idivMSVC는 8가지 추가 스토어/로드 명령어를 사용할 수 없습니다.

MSVC 인라인 asm(MSVC 및 icc에서 사용되며 일부 상용 컴파일러에서도 사용 가능):

  • asm을 보고 코드 스텝을 등록하는 것을 확인합니다.
  • 는 메모리를 통해서만 데이터를 전송할 수 있습니다.되어 있던 되며, 컴파일러에 됩니다.mov ecx, shift_count따라서 컴파일러가 생성하지 않는 단일 asm 명령을 사용하는 것은 출입할 때 메모리를 통해 왕복하는 것입니다.
  • 초보자 친화적이지만 데이터 입/출력의 오버헤드를 피할 수 없는 경우가 많습니다.구문의 제한뿐만 아니라 MSVC의 현재 버전에 있는 옵티마이저는 인라인 asm 블록을 중심으로 최적화하는 데도 능숙하지 않습니다.

GNU C 인라인 asm은 asm을 학습하는 좋은 방법이 아닙니다.asm을 잘 이해해야 컴파일러에게 코드를 알릴 수 있습니다.컴파일러가 알아야 할 것을 알아야 합니다.이 답변에는 다른 인라인 ASM 가이드 및 Q&A 링크도 포함되어 있습니다. 태그 Wiki에는 일반적으로 asm에 적합한 것이 많지만, GNU 인라인 asm에 대한 링크만 있습니다.(이 답변의 내용은 x86 이외의 플랫폼의 GNU 인라인 asm에도 적용됩니다.)

GNU C 인라인 asm 구문은 gcc, clang, icc 및 GNU C를 구현하는 일부 상용 컴파일러에 의해 사용됩니다.

  • 당신은 컴파일러에게 당신이 클럽하는 것을 말해야 합니다.이렇게 하지 않으면 디버깅하기 어려운 방법으로 주변 코드가 파손됩니다.
  • 및 에 입력의 및 출력의 를 알 수를 들어, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」, 「」 등입니다."c" (shift_count)가 '컴파일러'를 넣도록 .shift_count로 하다ecx실행되기 에 asm이 실행됩니다.
  • asm은 문자열 상수 안에 있어야 하기 때문에 큰 코드 블록의 경우 더 투박합니다.따라서 일반적으로 필요한 것은

    "insn   %[inputvar], %%reg\n\t"       // comment
    "insn2  %%reg, %[outputvar]\n\t"
    
  • 특히 오버헤드를 낮출 수 있습니다. 하나의 지시사항을 포장할 수 있습니다.(단일 명령어를 사용하는 것이 원래 설계 목적입니다.그래서 문제가 있는 경우 입출력에 동일한 레지스터를 사용하지 않도록 컴파일러에 초기 클로버에 대해 특별히 알려야 합니다.)


분할 「」: 「」)div)

32CPU 64 의 경우, 32경우, (32 x 32 - > 64) 경우, asm경우, gcc clang장점idiv★★★★★★에(int64_t)a / (int32_t)b32비트 레지스터에 결과가 맞지 않으면 명령이 실패하기 때문일 수 있습니다.따라서 1에서 몫과 나머지를 얻는 것에 대한 이 Q&A와는 달리, 이것은 인라인 asm의 사용 사례입니다(결과가 적합하다고 컴파일러에 통지하는 방법이 없는 한 idiv는 실패하지 않습니다).

일부합니다('arg'는 'arg'로 지정합니다hi오른쪽 레지스터에서도)와 같이 작은 함수를 삽입할 때 나타나는 것과 유사한 상황을 보여줍니다.


MSVC

inline-asm을 사용할 때는 register-arg 호출규칙에 주의하십시오.명백히 인라인 ASM 지원은 잘못 설계/실장되어 있기 때문에 컴파일러는 인라인 ASM 주위에 arg 레지스터를 저장/복원하지 않을있습니다.이 점을 지적해 주셔서 @RossRridge에 감사드립니다.

// MSVC.  Be careful with _vectorcall & inline-asm: see above
// we could return a struct, but that would complicate things
int _vectorcall div64(int hi, int lo, int divisor, int *premainder) {
    int quotient, tmp;
    __asm {
        mov   edx, hi;
        mov   eax, lo;
        idiv   divisor
        mov   quotient, eax
        mov   tmp, edx;
        // mov ecx, premainder   // Or this I guess?
        // mov   [ecx], edx
    }
    *premainder = tmp;
    return quotient;     // or omit the return with a value in eax
}

업데이트: 값in-inline에서도 non-void 함수의 끝부분( 를 사용하지 않음)에 그대로 두거나 그 끝부분에서 떨어지는 것이 지원됩니다.이 방법은 그 후에 코드가 없는 경우에만 작동한다고 생각합니다.asm진술.__asm{}; eax 값을 반환합니까? 참조하십시오.이것에 의해, 출력의 보존/재로드가 회피됩니다(적어도,quotient입력에 대해서는 아무것도 할 수 없습니다.스택 arg를 사용하는 비인라인 함수에서는 이미 메모리에 존재하지만 이 사용 예에서는 유용하게 인라인화할 수 있는 작은 함수를 씁니다.


MSVC 19.00.23026과 컴파일 완료/O2 (와 함께) 렉스터에main()exe 디렉토리를 검색하여 컴파일러의 asm 출력을 stdout에 덤프합니다).

## My added comments use. ##
; ... define some symbolic constants for stack offsets of parameters
; 48   : int ABI div64(int hi, int lo, int divisor, int *premainder) {
    sub esp, 16                 ; 00000010H
    mov DWORD PTR _lo$[esp+16], edx      ## these symbolic constants match up with the names of the stack args and locals
    mov DWORD PTR _hi$[esp+16], ecx

    ## start of __asm {
    mov edx, DWORD PTR _hi$[esp+16]
    mov eax, DWORD PTR _lo$[esp+16]
    idiv    DWORD PTR _divisor$[esp+12]
    mov DWORD PTR _quotient$[esp+16], eax  ## store to a local temporary, not *premainder
    mov DWORD PTR _tmp$[esp+16], edx
    ## end of __asm block

    mov ecx, DWORD PTR _premainder$[esp+12]
    mov eax, DWORD PTR _tmp$[esp+16]
    mov DWORD PTR [ecx], eax               ## I guess we should have done this inside the inline asm so this would suck slightly less
    mov eax, DWORD PTR _quotient$[esp+16]  ## but this one is unavoidable
    add esp, 16                 ; 00000010H
    ret 8

많은 추가 이동 명령이 있지만 컴파일러는 전혀 최적화되지 않습니다.이 보고 할 수 있을 라고 생각했다.mov tmp, edxasm 를 "asm", "asm", ""premainder 그렇게 로딩이 premainder인라인 asm 블록 전에 스택에서 레지스터로 변환됩니다.

기능은 실제로 더 나빠졌습니다._vectorcall일반적인 스택상의 ABI보다 더 많은 것을 사용할 수 있습니다.레지스터에 2개의 입력이 있는 경우 인라인 ASM이 이름 있는 변수에서 그것들을 로드할 수 있도록 메모리에 저장합니다.이것이 삽입되어 있으면 더 많은 파라미터가 레지스트리에 포함될 수 있으며 ASM에 메모리 오퍼랜드가 포함되도록 모든 파라미터를 저장해야 합니다. gcc와는 gcc에서는 이것을 해도 별로이 되지 .

*premainder = tmp된 더 뇌사상태의 를 피합니다.asm은 asm을 사용합니다.에 의해, 명령 가 합계 11( 「2」, 「2」, 「11」은 이 됩니다.ret를 참조해 주세요.

MSVC에서 가능한 한 최선의 코드를 꺼내려고 하고 있습니다.잘못해서 스트로맨 인수를 만드는 것이 아닙니다.그러나 AFAICT는 매우 짧은 시퀀스를 포장하는 것은 끔찍하다.64/32 -> 32 division에는 컴파일러가 이 특정 케이스에 적합한 코드를 생성할 수 있는 고유한 함수가 있을있으므로 MSVC에서 인라인 asm을 사용하는 전제는 모두 straw-man 인수가 될 수 있습니다.그러나 MSVC에서는 인라인 asm보다 내장 기능이 훨씬 우수하다는 것을 알 수 있습니다.


GNU C(gcc/clang/icc)

Gcc는 일반적으로 처음에 edx:eax의 64비트 정수를 생성하도록 이전 코드를 정렬할 수 있기 때문에 div64를 인라인화할 때 여기서 나타내는 출력보다 훨씬 더 성능이 좋습니다.

32개의 ABI가 있습니다.할 수 을 사용하는 경우 ASM은 입니다."rm"제약사항(godbolt 링크에서 시도: 제약사항에서 레지스터 옵션을 사용하는 대신 메모리를 통해 함수 arg를 바운스합니다).첫 번째 는 edx,64 MS 32입니다.스택을 또, 입니다).은, 「2」의 「2」의 「arg」의 의미입니다.★★★★★★★★★★★★★★★★★★,ret 8되어 있습니다MSVC 출력에 기재되어 있습니다.

// GNU C
// change everything to int64_t to do 128b/64b -> 64b division
// MSVC doesn't do x86-64 inline asm, so we'll use 32bit to be comparable
int div64(int lo, int hi, int *premainder, int divisor) {
    int quotient, rem;
    asm ("idivl  %[divsrc]"
          : "=a" (quotient), "=d" (rem)    // a means eax,  d means edx
          : "d" (hi), "a" (lo),
            [divsrc] "rm" (divisor)        // Could have just used %0 instead of naming divsrc
            // note the "rm" to allow the src to be in a register or not, whatever gcc chooses.
            // "rmi" would also allow an immediate, but unlike adc, idiv doesn't have an immediate form
          : // no clobbers
        );
    *premainder = rem;
    return quotient;
}

m32를 사용하면 godbolt 링크의 변경 내용에서 알 수 있듯이 3개의 로드, idiv 및 스토어만 얻을 수 있습니다.

mov     eax, ecx  # lo, lo
idivl  r9d      # divisor
mov     DWORD PTR [r8], edx       # *premainder_7(D), rem
ret

32비트 벡터 콜의 경우 gcc는 다음과 같은 처리를 합니다.

## Not real compiler output, but probably similar to what you'd get
mov     eax, ecx               # lo, lo
mov     ecx, [esp+12]          # premainder
idivl   [esp+16]               # divisor
mov     DWORD PTR [ecx], edx   # *premainder_7(D), rem
ret   8

MSVC는 gcc의 4와 비교하여 13개의 명령(재시도 제외)을 사용합니다. 것처럼 인라이닝에서는할 수 9개를 .(이나 로드는 premainder; 입력 3개 중 2개 정도는 저장해야 할 것 같습니다. 안에 로드하고 asm을 실행합니다.idiv는 2개의 출력을 저장하고 asm 외부에서 새로고침합니다.의 로드으로, 됩니다.)는 4개의 로드/스토어는 4개의 로드/스토어는 4개입니다

어떤 것을 사용하는지는 컴파일러에 따라 다릅니다.이것은 C언어처럼 표준어가 아닙니다.

asm__asm__GCC에서

asm와 함께 동작하지 않는다.-std=c99, 다음의 2개의 선택지가 있습니다.

  • 사용하다__asm__
  • 사용하다-std=gnu99

상세: 오류: 'asm' 미신고(이 함수에 처음 사용)

__asm__asm__GCC에서

어디에 있는지 찾을 수 없었다__asm는 문서화되어 있습니다(https://gcc.gnu.org/onlinedocs/gcc-7.2.0/gcc/Alternate-Keywords.html#Alternate-Keywords 에서 설명되지 않은 설명).단, GCC 8.1 소스에서는, 이러한 내용은 완전하게 같습니다.

  { "__asm",        RID_ASM,    0 },
  { "__asm__",      RID_ASM,    0 },

그래서 저는 그냥__asm__문서화되어 있습니다.

gcc 컴파일러에서는 큰 차이가 없습니다. asm또는__asm또는__asm__마찬가지로 네임스페이스의 목적 경합을 피하기 위해 사용합니다(asm이라는 이름을 붙이는 사용자 정의 함수가 있습니다).

언급URL : https://stackoverflow.com/questions/3323445/what-is-the-difference-between-asm-asm-and-asm

반응형