반응형


요즘 CPU 중에 SSE 지원 안되는게 거의 없을 것 같기는 하지만

CPU의 정보를 얻어와서 어떤 SIMD를 지원하는지 알아보기로 하자.


 
CPU 정보를 얻어오는 명령어는 CPUID 이다.

EAX값을 어떤 값으로 설정하느냐에 따라서 CPUID를 실행했을 때의 결과값이 바뀐다.

간단히 정리하면 아래와 같다.

EAX = 0 : 제조업체 ID 가져오기
EAX = 1 : 프로세서 정보 및 기능 비트
EAX = 2 : 캐시 및 TLB 서술자 정보
EAX = 3 : 프로세서 일련 번호
EAX = 80000000h : 가장 높은 확장 함수 가져오기
EAX = 80000001h : 확장 프로세서 정보 및 기능 비트
EAX = 80000002h,80000003h,80000004h         : 프로세서 브랜드 문자열
EAX = 80000005h : 1차 캐시 및 TLB ID
EAX = 80000006h : 확장 2차 캐시 기능
EAX = 80000007h : 고급 전원 관리 정보
EAX = 80000008h : 가상 및 물리 주소 크기

우리가 체크해보려고 하는 것은 다음과 같다.

1. 생산자 이름
2. SSE 지원 여부
3. SSE2 지원 여부
4. MMX 지원 여부
5. 3DNow 지원 여부
6. 확장 기능을 지원하는가?
7. MMX 확장 지원 여부
8. 3DNow 확장 지원 여부


struct SMSCPUInfo
{
    bool    sse_;         // Streaming SIMD Extension
    bool    sse2_;        // Streaming SIMD Extension 2
    bool    threeNow_;    // 3DNow!
    bool    mmx_;         // MMX
    
    bool    ext_;         // 확장된 기능이 사용가능한가?
    bool    mmxExt_;      // MMX 
    bool    threeNowExt_; // 3DNow!
    
    char    vendor_[13];    // 생산자 이름
};

다음과 같은 구조체가 있다고 생각하고 이후 진행을 하겠다.

하나씩 해보자.


 
1. 생산자 이름

위의 설명에서 보면 나오듯이 EAX에 0을 넣고 CPUID를 호출해 주면 나온다.

생산자 이름은 12바이트로 3개의 레지스터에 걸쳐서 나온다.

순서대로 적으면 ebx, edx, ecx 순으로 저장해야 알맞게 나온다.

CHAR* vendorName = cpuInfo.vendor_;
mov        eax,                    0            // 생산자 이름을 얻을 수 있다.    
CPUID                                

mov        esi,                    vendorName   // 포인터 연결     
mov        [esi],                  ebx          // 처음 4바이트  
mov        [esi + 4],              edx          // 두번째 4바이트
mov        [esi + 8],              ecx          // 세번째 4바이트


2. SSE 지원여부
3. SSE2 지원여부
4. MMX 지원여부


3개를 같이 하는 이유는 같은 방식으로 구하기 때문이다.

EAX에 1을 넣고 CPUID를 호출해 주면 edx에 값이 변경되고 이 값을 가지고 비교해서 각각 지원하는지 알 수 있다. 
mov        eax,                    1           // 지원 기능들을 얻어온다.
CPUID

test       edx,                    04000000h   // SSE2를 지원하는가?
jz        __NOSSE2                             // 지원하지 않는다면 __NOSSE2로 넘어간다.
mov        [cpuInfo.sse2_],        1           // 지원하면 sse2_값을 1로 변경한다.

__NOSSE2:
test       edx,                    02000000h   // SSE를 지원하는가?
jz        __NOSSE                              // 지원하지 않는다면 __NOSSE로 넘어간다.
mov        [cpuInfo.sse_],         1           // 지원하면 sse_값을 1로 변경한다.
__NOSSE:
test       edx,                    00800000h   // MMX를 지원하는가?
jz        __NOMMX                              // 지원하지 않느다면 __NOMMX로 넘어간다.
mov        [cpuInfo.mmx_],         1           // 지원하면 mmx_값을 1로 변경한다.
__NOMMX:
위의 소스대로 edx 값과 04000000h 와 같은 특정 비트 값들을 &연산으로 비교해봐서

1이면 지원하는거고 0이면 지원하지 않는것으로 판단한다.


* 예외처리  
__try
{
    __asm
    {
        mov        eax,            0                
    }
}
_except(EXCEPTION_EXECUTE_HANDLER)                          // 실행중 예외가 발생했다면                  
{
    if (STATUS_ILLEGAL_INSTRUCTION ==_exception_code())     // 예외 코드가 합법적이지않은 명령어
    {                                                       // 였다면 처리.. 여기서는 리턴
        return cpuInfo;                                    
    }
}    


5. 3DNow 지원 여부
6. 확장 기능을 지원하는가?


이 처리도 위와 다를것은 없지만 eax값이 좀 다르므로 따로 설명하겠다.

eax에 80000000h를 넣고 CPUID를 호출해주면 확장 함수와 관련된 내용을 얻어올 수 있다.
eax에 80000001h를 넣고 CPUID를 호출해주면 확장 프로세서 정보를 얻어올 수 있다.

이를 이용해 위의 두가지 정보를 얻어오자.  
__asm
{
    mov        eax,                     80000000h         // 확장 함수 정보를 얻어온다.
    CPUID

    cmp        eax,                     80000000h         // eax값과 80000000h값의 차이를 구한다.
    jbe        __NOEXT                                    // 작거나 같으면 __NOEXT로 넘어간다.
    mov        [cpuInfo.ext_],          1                 // 아니라면 ext_를 1로 변경한다.

    mov        eax,                     80000001h         // 확장 프로세서 정보를 얻어온다.
    CPUID

    test       edx,                     80000000h         // 지원 정보가 있는가?
    jz        __NOEXT                                     // 없다면 __NOEXT로 넘어간다.
    mov        [cpuInfo.threeNow_],     1                 // 있다면 threeNow_를 1로 변경한다.
__NOEXT:

7. MMX 확장 지원 여부
8. 3DNow 확장 지원 여부


AMD 프로세서에 해당하는 이야기이므로 AMD인지 구분해야 한다.

if (0 == strncmp(cpuInfo.vendor_, "AuthenticAMD", 12) && cpuInfo.ext_) // AMD CPU인가?

mov        eax,                    0x80000001          // 확장 함수 정보를 얻어온다.  
CPUID

test       edx,                     0x40000000         // 3DNow 확장을 지원하는가?
jz         __NOEXT3DNOW                                // 지원하지 않으면 __NOEXT3DNOW로 넘어간다.
mov        [cpuInfo.threeNowExt_], 1                   // 지원하면 threeNowExt_를 1로 변경한다.
__NOEXT3DNOW:
test       edx,                     0x00400000         // MMX 확장을 지원하는가?
jz         __NOEXTMMX                                  // 지원하지 않으면 __NOEXTMMX로 넘어간다.  
mov        [cpuInfo.mmxExt_],      1                   // 지원하면 mmxExt_를 1로 변경한다.
__NOEXTMMX:


여기까지가 CPU정보를 통해서 SSE등이 지원되는지 알아보았다.

하지만 한가지 더 체크해야 할 것이있다.

바로 OS이다.

CPU가 지원되지만 OS에서 명령어셋을 지원하지 않을 수도 있다.
__try
{
    __asm xorps    xmm0, xmm0                            // SSE 명령어 한번 쳐리해보고                           
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
    if (STATUS_ILLEGAL_INSTRUCTION == _exception_code()) // 지원되지 않는다는 예외가 오면 지원 안함   
        return false;

    return true;                                        // 아니면 지원함  
}
 상당히 쿨한 방식으로 판단할 수 있다.


 
CPU와 OS에서 지원하면 SSE를 지원한다고 판단하고 IDE에 SSE 명령까지 쓴다고 설정만 해놓고 코딩하면된다.

이것을 판단하는 이유는 수학 라이브러리가 SSE가 되는 곳에서만 돌아가면 안되기 때문에 지원 여부를 bool값으로 두고

그 값을 통해서 SSE 명령어를 쓸것인가 아니면 일반 연산자를 쓸 것인가 판단하기 위함이다.

이제 본격적으로 SSE를 통해서 수학 라이브러리를 하나씩 만들어 보자.


 
반응형
Posted by msparkms
,