부문/곱셈 지침 :이 RAX (결과)와 RDX (나머지)에서 적절한 결과를 제공 피연산자는 이고 서명되지 않은 코드는mul
/div
입니다. 당신이 정말 따라서 수행하는 작업은 다음과 같습니다
- 서명
-566
(0xfffffdca
로 2 보수 32 비트)는이 17592183726080
결과 4096
(EDX:EAX
에서 0xfff:0xffdca000
)를 곱하는 부호 4294958538
- 로 해석됩니다. 당신이)
- 전체 64 비트 값이 상위 32 비트가
0xfff
, 4095
을 사실에 400
이 아니라 인해 나누어을 "기대"로 그의 낮은 32 비트가 -2318336
로 변환주의, 결과는 UINT32_MAX
를 초과하고 예외입니다 높인.당신이 divl
전에 xor %%edx,%%edx
를 삽입하여 상위 32 비트를 취소하면
작업이 성공하지만 당신이 기대하지 않는 것이 돌아갑니다 - 0xa3c066
결과 400
에 의해 즉, 그것은 0xffdca000
(4292648960
를) 분할을 (10731622
)가 EAX
, 나머지가 0xa0
(160
)이 EDX
이다.
기계가 지시 한 내용까지는 "정확하다"는 것이지만 예상 한 것은 아닙니다. 서명 된 번호를 사용하려면 imul
/idiv
이 필요합니다.
조립체 궁극적 다음으로 간략화 될 수있다 :
GCC 입력/출력으로 사용하는 레지스터를 지정할 수 있기 때문이다
__asm__ __volatile__ (
"imull %3 \n"
"idivl %4 \n"
: "=a" (nRet),
"=&d" (nMod)
: "a" (nNumber),
"mr" (nNumerator),
"mr" (nDenominator)
: "cc"
);
때문에 데이터가없는 이동은 모두 여기에 필요하다. 또한, "m"
제약 조건만으로도 인수를 스택에 강제 적용 할 때 64 비트에 차선 최적화 코드가 생성됩니다. 대안을 주면 생성 된 코드가 개선 될 것입니다.
편집 :nMod
제약을 "=&d"(nMod)
으로 변경했습니다. gcc가 "초기 clobber"를 호출해야합니다. 즉, 모든 입력 피연산자가 사용/사용되기 전에 지정된 출력 레지스터가 덮어 쓰여지고 컴파일러에서 EDX
에 입력 (특히 (nDenominator)
)을 전달하지 않도록 지시합니다. 그렇지 않으면 일어날 것이라고, 그것은 "흥미로운"실패 모드를 일으킬 것입니다. 에만 인 경우 "m"
을 사용하면 nNumerator
/nDenominator
에 문제가되지 않지만 레지스터가 허용되면주의해야합니다.
Edit2 : 위의 코드는 물론 오버플로 예외에 대한 증거는 아닙니다. 당신은 여전히 MulDivRound(INT32_MAX, 4, 2)
처럼 그것들을 방아쇠를 당길 수 있습니다. 정당하게/당연히이 지시는 디자인된다. 그런 일이 발생하지 않도록해야한다면, [i]div
앞에있는 분모와 EDX
/RDX
을 비교하고 더 작은 경우를 처리하는 코드를 추가해야합니다.
디버거에서 코드를 단계별 실행하여 각 명령어 앞에 레지스터 값을 확인하십시오. – Michael