2017-01-06 26 views
1

我想通用的方式來檢測特定的CPU功能。對於這個任務,我創建了這個函數,它帶有EAX葉號,註冊名和位號,並返回true或false。它適用於MMX/SSEx/AVX(EAX = 1),但它不檢測AVX2(EAX = 7)。代碼檢測MMX/SSE/AVX但不是AVX2

CPU:i5-4670k 操作系統:Windows 7

DetectCPUFeature('1','EDX',23) //DETECTS MMX CORRECTLY 
DetectCPUFeature('1','EDX',25) //DETECTS SSE CORRECTLY 
DetectCPUFeature('1','EDX',26) //DETECTS SSE2 CORRECTLY 
DetectCPUFeature('1','ECX',0) //DETECTS SSE3 CORRECTLY 
DetectCPUFeature('1','ECX',9) //DETECTS SSSE3 CORRECTLY 
DetectCPUFeature('1','ECX',19) //DETECTS SSE4.1 CORRECTLY 
DetectCPUFeature('1','ECX',20) //DETECTS SSE4.2 CORRECTLY 
DetectCPUFeature('1','ECX',28) //DETECTS AVX CORRECTLY 

DetectCPUFeature('7','EBX',5) //DOES NOT DETECT AVX2! 

function DetectCPUFeature(EAX_Leaf_HEX,Register_Name:string;Bit:byte):boolean; 
var _eax,_ebx,_ecx,_edx,EAX_Leaf,_Result: Longword; 
    x:integer; 
    Binary_mask:string; 
    Decimal_mask:int64; 
begin 

    EAX_Leaf:=HexToInt(EAX_Leaf_HEX); 
    Binary_mask:='1'; 
    for x:=1 to Bit do Binary_mask:=Binary_mask+'0'; 
    Decimal_mask:=BinToInt(Binary_mask); 

    if AnsiUpperCase(Register_Name)='EDX' then 
    begin 
    asm 
     mov eax,EAX_Leaf // https://en.wikipedia.org/wiki/CPUID 
     db $0F,$A2 // db $0F,$A2 = CPUID instruction 
     mov _Result,edx 
    end; 
    end; 

    if AnsiUpperCase(Register_Name)='ECX' then 
    begin 
    asm 
     mov eax,EAX_Leaf 
     db $0F,$A2 
     mov _Result,ecx 
    end; 
    end; 

    if AnsiUpperCase(Register_Name)='EBX' then 
    begin 
    asm 
     mov eax,EAX_Leaf 
     db $0F,$A2 
     mov _Result,ebx 
    end; 
end; 

if (_Result and Decimal_mask) = Decimal_mask then DetectCPUFeature:=true 
else DetectCPUFeature:=false; 

end; 

回答

5

這種代碼非常可疑,混合asm和Pascal代碼。您的代碼在asm塊中修改了寄存器並且無法恢復它們。這可能很容易與編譯器的註冊使用衝突。我對你的強烈建議是,你不應該以這種方式混合asm和Pascal。始終使用純粹的帕斯卡或純粹的阿斯姆。

您需要的是執行CPUID指令並返回結構中所有寄存器的函數。然後,您可以使用Pascal代碼挑選出您想要的內容。

另外,正如@J ...指出的那樣,在調用CPUID指令之前,需要在ECX寄存器中指定子葉值。這是對最近增加的一些論點的要求。

這是你需要的功能:

type 
    TCPUID = record 
    EAX: Cardinal; 
    EBX: Cardinal; 
    ECX: Cardinal; 
    EDX: Cardinal; 
    end; 

function GetCPUID(Leaf, Subleaf: Cardinal): TCPUID; 
asm 
    push ebx 
    push edi 
    mov edi, ecx 
    mov ecx, edx 
    cpuid 
    mov [edi+$0], eax 
    mov [edi+$4], ebx 
    mov [edi+$8], ecx 
    mov [edi+$c], edx 
    pop edi 
    pop ebx 
end; 

我寫這32位代碼,但如果你需要支持64位代碼也是支持是很容易的補充。

function GetCPUID(Leaf, Subleaf: Integer): TCPUID; 
asm 
{$IF Defined(CPUX86)} 
    push ebx 
    push edi 
    mov edi, ecx 
    mov ecx, edx 
    cpuid 
    mov [edi+$0], eax 
    mov [edi+$4], ebx 
    mov [edi+$8], ecx 
    mov [edi+$c], edx 
    pop edi 
    pop ebx 
{$ELSEIF Defined(CPUX64)} 
    mov r9,rcx 
    mov ecx,r8d 
    mov r8,rbx 
    mov eax,edx 
    cpuid 
    mov [r9+$0], eax 
    mov [r9+$4], ebx 
    mov [r9+$8], ecx 
    mov [r9+$c], edx 
    mov rbx, r8 
{$ELSE} 
    {$Message Fatal 'GetCPUID has not been implemented for this architecture.'} 
{$IFEND} 
end; 

有了這個在眼前,你可以調用CPUID傳遞任何值作爲輸入,檢索所有的4個寄存器的輸出,通過它可以那麼做你請。

您創建位掩碼的代碼效率極低,遠非慣用。使用1 shl N在位置N中創建一個具有單個位集的值。

這樣的代碼:

if (_Result and Decimal_mask) = Decimal_mask then DetectCPUFeature:=true 
else DetectCPUFeature:=false; 

也是從地道某種方式。這通常會被寫成這樣:

DetectCPUFeature := value and mask <> 0; 

你可能最終得到的包裝功能,看起來像這樣:

type 
    TCPUIDRegister = (regEAX, regEBX, regECX, regEDX); 

function GetCPUIDRegister(CPUID: TCPUID; Reg: TCPUIDRegister): Cardinal; 
begin 
    case Reg of 
    regEAX: 
    Result := CPUID.EAX; 
    regEBX: 
    Result := CPUID.EBX; 
    regECX: 
    Result := CPUID.ECX; 
    regEDX: 
    Result := CPUID.EDX; 
    end; 
end; 

function CPUFeatureEnabled(Leaf, Subleaf: Cardinal; Reg: TCPUIDRegister; Bit: Integer): Boolean; 
var 
    value: Cardinal; 
begin 
    value := GetCPUIDRegister(GetCPUID(Leaf, Subleaf), Reg); 
    Result := value and (1 shl Bit) <> 0; 
end; 
2

雖然大衛的答案是優秀的,則操作失敗的原因是ECX寄存器未設置爲零(在CPUID調用中獲取擴展信息時需要)。

見:How to detect New Instruction support in the 4th generation Intel® Core™ processor family

其中AVX2由(重點煤礦)

CPUID實測值(EAX = 07H,ECX = 0H):EBX。AVX2 [bit 5] == 1

以下正確返回擴展信息並標識AVX2支持。

if AnsiUpperCase(Register_Name)='EBX' then 
    begin 
    asm 
     push ecx   { push ecx to stack} 
     mov ecx, 0  { set ecx to zero} 
     mov eax,EAX_Leaf 
     db $0F,$A2 
     mov _Result,ebx 
     pop ecx   { restore ecx} 
    end; 

其他asm函數具有相同的錯誤ECX需要爲這些呼叫也爲零。

+0

'ECX'是在大小爲32個比特,即使在64位機器上,所以'tmp'變量應該是一個32位的變量相匹配。 'NativeInt'不適合用於這個目的,因爲它是64位可執行文件中的64位。改用'(U)Int32'或'Cardinal'。 –

+0

@RemyLebeau是的,好點。可能應該只使用堆棧,真的。 –

-1

從Synopse INFORMATIQUE採取:

type 
    /// the potential features, retrieved from an Intel CPU 
    // - see https://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits 
    TALIntelCpuFeature = 
    ({ in EDX } 
    cfFPU, cfVME, cfDE, cfPSE, cfTSC, cfMSR, cfPAE, cfMCE, 
    cfCX8, cfAPIC, cf_d10, cfSEP, cfMTRR, cfPGE, cfMCA, cfCMOV, 
    cfPAT, cfPSE36, cfPSN, cfCLFSH, cf_d20, cfDS, cfACPI, cfMMX, 
    cfFXSR, cfSSE, cfSSE2, cfSS, cfHTT, cfTM, cfIA64, cfPBE, 
    { in ECX } 
    cfSSE3, cfCLMUL, cfDS64, cfMON, cfDSCPL, cfVMX, cfSMX, cfEST, 
    cfTM2, cfSSSE3, cfCID, cfSDBG, cfFMA, cfCX16, cfXTPR, cfPDCM, 
    cf_c16, cfPCID, cfDCA, cfSSE41, cfSSE42, cfX2A, cfMOVBE, cfPOPCNT, 
    cfTSC2, cfAESNI, cfXS, cfOSXS, cfAVX, cfF16C, cfRAND, cfHYP, 
    { extended features in EBX, ECX } 
    cfFSGS, cf_b01, cfSGX, cfBMI1, cfHLE, cfAVX2, cf_b06, cfSMEP, cfBMI2, 
    cfERMS, cfINVPCID, cfRTM, cfPQM, cf_b13, cfMPX, cfPQE, cfAVX512F, 
    cfAVX512DQ, cfRDSEED, cfADX, cfSMAP, cfAVX512IFMA, cfPCOMMIT, 
    cfCLFLUSH, cfCLWB, cfIPT, cfAVX512PF, cfAVX512ER, cfAVX512CD, 
    cfSHA, cfAVX512BW, cfAVX512VL, cfPREFW1, cfAVX512VBMI); 

    /// all features, as retrieved from an Intel CPU 
    TALIntelCpuFeatures = set of TALIntelCpuFeature; 

var 
    /// the available CPU features, as recognized at program startup 
    ALCpuFeatures: TALIntelCpuFeatures; 

{**} 
type 
_TRegisters = record 
    eax,ebx,ecx,edx: cardinal; 
end; 

{***************************************************************} 
procedure _GetCPUID(Param: Cardinal; var Registers: _TRegisters); 
{$IF defined(CPU64BITS)} 
asm // ecx=param, rdx=Registers (Linux: edi,rsi) 
    .NOFRAME 
    mov  eax, ecx 
    mov  r9, rdx 
    mov  r10, rbx // preserve rbx 
    xor  ebx, ebx 
    xor  ecx, ecx 
    xor  edx, edx 
    cpuid 
    mov  _TRegisters(r9).&eax, eax 
    mov  _TRegisters(r9).&ebx, ebx 
    mov  _TRegisters(r9).&ecx, ecx 
    mov  _TRegisters(r9).&edx, edx 
    mov  rbx, r10 
end; 
{$else} 
asm 
    push esi 
    push edi 
    mov  esi, edx 
    mov  edi, eax 
    pushfd 
    pop  eax 
    mov  edx, eax 
    xor  eax, $200000 
    push eax 
    popfd 
    pushfd 
    pop  eax 
    xor  eax, edx 
    jz  @nocpuid 
    push ebx 
    mov  eax, edi 
    xor  ecx, ecx 
    cpuid 
    mov  _TRegisters(esi).&eax, eax 
    mov  _TRegisters(esi).&ebx, ebx 
    mov  _TRegisters(esi).&ecx, ecx 
    mov  _TRegisters(esi).&edx, edx 
    pop  ebx 
@nocpuid: 
    pop  edi 
    pop  esi 
end; 
{$ifend} 

{******************************} 
procedure _TestIntelCpuFeatures; 
var regs: _TRegisters; 
begin 
    regs.edx := 0; 
    regs.ecx := 0; 
    _GetCPUID(1,regs); 
    PIntegerArray(@ALCpuFeatures)^[0] := regs.edx; 
    PIntegerArray(@ALCpuFeatures)^[1] := regs.ecx; 
    _GetCPUID(7,regs); 
    PIntegerArray(@ALCpuFeatures)^[2] := regs.ebx; 
    PByteArray(@ALCpuFeatures)^[12] := regs.ecx; 
end; 

initialization 
    _TestIntelCpuFeatures; 
+0

我猜這個代碼是受[MPL/GPL/LGPL tri-license]保護的(https://synopse.info/forum/viewtopic.php?id=25)。 Ping @ArnaudBouchez。 –

+0

是的,https://synopse.info/forum/viewtopic.php?id=27 :) – loki

+3

這篇文章違反該許可證 –