cpuinfo module

Share your advanced PureBasic knowledge/code with the community.
User avatar
idle
Always Here
Always Here
Posts: 5049
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

cpuinfo module

Post by idle »

CPUInfo module, please add to it or improve it

asm and c backend for x64/x86

Code: Select all

DeclareModule CPUInfo
  ;cpu info utility module x86/x64 asm c backends 
  ;idle  
  ;v1.1 added GetCPUSerialNumber - jassing 
  
  Structure CPUInfoString 
    val.i
    desc.s 
  EndStructure 
  
  Global  NewMap cpudata.CPUInfoString()   
  cpudata("CPU_FPU")\val =0 : cpudata()\desc ="Onboard x87 FPU"
  cpudata("CPU_VME")\val = 1 :cpudata()\desc = "Virtual 8086 mode extensions (such As VIF, VIP, PIV)"
  cpudata("CPU_DE")\val =2 : cpudata()\desc=  "Debugging extensions (CR4 bit 3)"
  cpudata("CPU_PSE")\val =3 : cpudata()\desc=   "Page Size Extension"   
  cpudata("CPU_TSC")\val =4 : cpudata()\desc=    "Time Stamp Counter"   
  cpudata("CPU_MSR")\val=5 : cpudata()\desc=   "Model-specific registers"
  cpudata("CPU_PAE")\val=6 : cpudata()\desc=    "Physical Address Extension"
  cpudata("CPU_MCE")\val=7 : cpudata()\desc=   "Machine Check Exception"
  cpudata("CPU_CX8")\val =8 : cpudata()\desc=    "CMPXCHG8 (compare-And-Swap) instruction"   
  cpudata("CPU_APIC")\val=9  : cpudata()\desc=   "Onboard Advanced Programmable Interrupt Controller"   
  
  cpudata("CPU_SEP")\val=11 : cpudata()\desc=   "SYSENTER And SYSEXIT instructions"
  cpudata("CPU_MTRR")\val=12 : cpudata()\desc=    "Memory Type Range Registers"   
  cpudata("CPU_PGE")\val =13 : cpudata()\desc=   "Page Global Enable bit in CR4"   
  cpudata("CPU_MCA")\val =14 : cpudata()\desc=   "Machine check architecture"   
  cpudata("CPU_CMOV")\val=15 : cpudata()\desc=   "Conditional move And FCMOV instructions"   
  cpudata("CPU_PAT")\val=16  : cpudata()\desc=  "Page Attribute Table   (reserved)"
  cpudata("CPU_PSE36")\val=17 : cpudata()\desc=  "36-bit page size extension"
  cpudata("CPU_PSN")\val=18 : cpudata()\desc=   "Processor Serial Number"
  cpudata("CPU_CLFSH")\val=19 : cpudata()\desc=   "CLFLUSH instruction (SSE2)"
  
  cpudata("CPU_DS")\val=21  : cpudata()\desc=  "Debug store: save trace of executed jumps"
  cpudata("CPU_ACPI")\val=22 : cpudata()\desc=    "Onboard thermal control MSRs For ACPI"
  cpudata("CPU_MMX")\val=23 : cpudata()\desc=   "MMX instructions"   
  cpudata("CPU_FXSR")\val=24 : cpudata()\desc=    "FXSAVE, FXRESTOR instructions, CR4 bit 9"
  cpudata("CPU_SSE")\val=25  : cpudata()\desc=  "SSE instructions (a.k.a. Katmai New Instructions)"
  cpudata("CPU_SSE2")\val=26 : cpudata()\desc=   "SSE2 instructions"   
  cpudata("CPU_SS")\val=27  : cpudata()\desc=  "CPU cache supports self-snoop"   
  cpudata("CPU_HTT")\val=28 : cpudata()\desc=   "Hyper-threading"   
  cpudata("CPU_TM")\val=29  : cpudata()\desc=  "Thermal monitor automatically limits temperature"
  cpudata("CPU_IA64")\val=30 : cpudata()\desc=    "IA64 processor emulating x86"   
  cpudata("CPU_PBE")\val=31 : cpudata()\desc=   "Pending Break Enable (PBE#PB_CPU_ pin) wakeup support"
  
  cpudata("CPU_SSE3")\val=32 : cpudata()\desc=    "Prescott New Instructions-SSE3 (PNI)"
  cpudata("CPU_PCMULQDQ")\val =33 : cpudata()\desc=   "PCLMULQDQ support"
  cpudata("CPU_DTES64")\val=34 : cpudata()\desc=   "64-bit Debug store (edx bit 21)"
  cpudata("CPU_MONITOR")\val=35 : cpudata()\desc=    "MONITOR And MWAIT instructions (SSE3)"
  cpudata("CPU_DSCPL")\val=36 : cpudata()\desc=   "CPL qualified Debug store"
  cpudata("CPU_VMX")\val=37 : cpudata()\desc=   "Virtual Machine eXtensions"
  cpudata("CPU_SMX")\val=38 : cpudata()\desc=   "Safer Mode Extensions (LaGrande)"
  cpudata("CPU_EST")\val=39 : cpudata()\desc=   "Enhanced SpeedStep"
  cpudata("CPU_TM2")\val=40 : cpudata()\desc=   "Thermal Monitor 2"
  cpudata("CPU_SSSE3")\val=41 : cpudata()\desc=   "Supplemental SSE3 instructions"
  cpudata("CPU_CNXTID")\val=42 : cpudata()\desc=    "L1 Context ID"
  
  cpudata("CPU_FMA ")\val=44  : cpudata()\desc= "Fused multiply-add (FMA3)"
  cpudata("CPU_CX16")\val=45  : cpudata()\desc=  "CMPXCHG16B instruction"
  cpudata("CPU_XTPR")\val=46  : cpudata()\desc=  "Can disable sending task priority messages"
  cpudata("CPU_PDCM")\val=47  : cpudata()\desc=  "Perfmon & Debug capability"
  
  cpudata("CPU_PCID")\val=49 : cpudata()\desc=   "Process context identifiers (CR4 bit 17)"
  cpudata("CPU_DCA")\val=50 : cpudata()\desc=   "Direct cache access For DMA writes[10][11]"
  cpudata("CPU_SSE41")\val=51 : cpudata()\desc=   "SSE4.1 instructions"
  cpudata("CPU_SSE42")\val=52 : cpudata()\desc=   "SSE4.2 instructions"
  cpudata("CPU_X2APIC")\val=53 : cpudata()\desc=    "x2APIC support"
  cpudata("CPU_MOVBE")\val=54 : cpudata()\desc=   "MOVBE instruction (big-endian)"
  cpudata("CPU_POPCNT")\val=55 : cpudata()\desc=    "POPCNT instruction"
  cpudata("CPU_TSCDEADLINE")\val=56 : cpudata()\desc=   "APIC supports one-shot operation using a TSC deadline value"
  cpudata("CPU_AES")\val=57 : cpudata()\desc=   "AES instruction set"
  cpudata("CPU_XSAVE")\val=58 : cpudata()\desc=   "XSAVE, XRESTOR, XSETBV, XGETBV"
  cpudata("CPU_OSXSAVE")\val=59 : cpudata()\desc=   "XSAVE enabled by OS"
  cpudata("CPU_AVX")\val=60 : cpudata()\desc=   "Advanced Vector Extensions"
  cpudata("CPU_F16C")\val=61 : cpudata()\desc=    "F16C (half-precision) FP support"
  cpudata("CPU_RDRND")\val=62 : cpudata()\desc=    "RDRAND (on-chip random number generator) support"
  cpudata("CPU_HYPERVISOR")\val=63 : cpudata()\desc=   "Running on a hypervisor (always 0 on a real CPU, but also With some hypervisors)"
  
  Declare.s GetVendor()
  Declare.s GetName() 
  Declare.s GetVendorVM()
  Declare.s GetCPUSerialNumber()
  Declare   IsCPU(strOp.s) 
  Declare   CountCores()
  Declare   GetActiveCore()
  Declare   GetFrequency()  
  Declare.s GetAllFeatures() 
  
EndDeclareModule 

Module CPUInfo 
  
  CompilerIf #PB_Compiler_Backend = #PB_Backend_C  

    Macro AsmInput(var) 
      !:[var] "r" (v_#var) 
    EndMacro  
    
    Macro AsmInput2(var0,var1) 
      !:[var0] "r" (v_#var0),[var1] "r" (v_#var1) 
    EndMacro  
    
    Macro AsmInput3(var0,var1,var2) 
      !:[var0] "r" (v_#var0),[var1] "r" (v_#var1),[var2] "r" (v_#var2)  
    EndMacro
    
    Macro AsmOutput(var)
      !".att_syntax;"
      !:[var] "=r" (v_#var)
    EndMacro  
    
    Macro AsmOutput2(var0,var1)
      !".att_syntax;"
      !:[var0] "=r" (v_#var0), [var1] "=r" (v_#var1) 
    EndMacro  
    
    Macro AsmOutput3(var0,var1,var2)
      !".att_syntax;"
      !:[var0] "=r" (v_#var0) , [var1] "=r" (v_#var1) , [var2] "=r" (v_#var2)
    EndMacro  
    
    Macro AsmOutput4(var0,var1,var2,var3)
      !".att_syntax;"
      !:[var0] "=r" (v_#var0) , [var1] "=r" (v_#var1) , [var2] "=r" (v_#var2) , [var3] "=r" (v_#var3)
    EndMacro  
       
    Macro BeginAsm() 
      ! __asm__ __volatile__ (".intel_syntax noprefix;"
    EndMacro
       
    Macro AsmClobbers() ; "eax","edx" ...
      !:
    EndMacro   
    
    Macro EndAsm() 
      !);
    EndMacro   
    
    Macro __cpuid(level,va, vb, vc, vd)
      !unsigned int reg_a, reg_b, reg_c, reg_d;
      !asm volatile ("cpuid;"  
      !: "=a" (reg_a), "=b" (reg_b), "=c" (reg_c), "=d" (reg_d)	
      !: "0" (v_level)
      !);
      ! v_#va = reg_a;
      ! v_#vb = reg_b;
      ! v_#vc = reg_c;
      ! v_#vd = reg_d;
    EndMacro 
    
  CompilerEndIf 
    
  Procedure.s GetVendor()
    Protected a.l,b.l,c.l,d.l,level.l=0
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C  
       __cpuid(level,a,b,c,d)
       ProcedureReturn PeekS(@b,4,#PB_Ascii) + PeekS(@d,4,#PB_Ascii) + PeekS(@c,4,#PB_Ascii)
    CompilerElse 
      !mov eax,0
      !cpuid 
      !mov [p.v_a],ebx
      !mov [p.v_b],edx 
      !mov [p.v_c],ecx 
      ProcedureReturn PeekS(@a,4,#PB_Ascii) + PeekS(@b,4,#PB_Ascii) + PeekS(@c,4,#PB_Ascii)
   CompilerEndIf 
     
  EndProcedure   
  
  Procedure.s GetVendorVM()
    Protected a.l,b.l,c.l,d.l,level.l
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C  
      level = $40000000
      __cpuid(level,a,b,c,d)
     ProcedureReturn PeekS(@b,4,#PB_Ascii) + PeekS(@c,4,#PB_Ascii) + PeekS(@d,4,#PB_Ascii) 
    CompilerElse 
      !mov eax,$40000000
      !cpuid 
      !mov [p.v_a],ebx
      !mov [p.v_b],ecx 
      !mov [p.v_c],edx
    CompilerEndIf 
    ProcedureReturn PeekS(@a,4,#PB_Ascii) + PeekS(@b,4,#PB_Ascii) + PeekS(@c,4,#PB_Ascii)
  EndProcedure   
  
  Procedure IsCPU(strOp.s)
    Protected op.l,ret.l 
    op = cpudata(strOp)\val
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C  
    BeginAsm() 
    !"mov eax, 1;"
    !"cpuid;"
    !"xchg eax, ecx;"
    !"mov ecx, %[op];"
    !"shr edx, cl;"
    !"shr eax, cl;"
    !"sub ecx, 32;"
    !"shr ecx, 31;"
    !"and edx, ecx;"
    !"xor ecx, 1;"
    !"and eax, ecx;"
    !"or eax, edx;"
    !"mov %[ret],eax;"
    AsmOutput(ret)
    AsmInput(op)
    AsmClobbers() "eax","ecx","edx"
    EndAsm() 
  CompilerElse 
    !mov eax, 1
    !cpuid
    !xchg eax, ecx
    !mov ecx, [p.v_op]
    !shr edx, cl
    !shr eax, cl
    !sub ecx, 32
    !shr ecx, 31
    !and edx, ecx
    !xor ecx, 1
    !and eax, ecx
    !or eax, edx
    !mov [p.v_ret],eax 
    
    CompilerEndIf
    
    ProcedureReturn ret   
  EndProcedure 
  
  Procedure.s GetAllFeatures() 
    Protected output.s  
    ForEach cpudata() 
      If IsCPU(MapKey(cpudata())) 
        output + MapKey(cpudata()) + " :: " + cpudata()\desc + #LF$ 
      EndIf  
    Next 
    ProcedureReturn output 
  EndProcedure 
  
  Procedure.s GetName()
    ProcedureReturn CPUName()
  EndProcedure 
  
  Procedure CountCores()
    ProcedureReturn CountCPUs(#PB_System_ProcessCPUs)
  EndProcedure 
  
  Procedure.i GetActiveCore()
    Protected ret.l 
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C  
      BeginAsm() 
      !"xor ebx, ebx;"
      !"mov eax, 0x1;"
      !"cpuid;"
      !"mov eax,ebx;" 
      !"shr eax,24;"
      !"mov %[ret],eax;" 
      AsmOutput(ret) 
      !:
      AsmClobbers() "eax","ebx" 
      EndAsm() 
    CompilerElse
      !xor ebx, ebx
      !mov eax, 0x1
      !cpuid
      !mov eax,ebx 
      !shr eax,24
      !mov [p.v_ret],eax 
    CompilerEndIf
    
    ProcedureReturn ret 
  EndProcedure
  
  Procedure GetFrequency() 
    
    Protected a.l,b.l,t1.l,t2.l,result 
    Repeat     
      
      CompilerIf #PB_Compiler_Backend = #PB_Backend_C  
        
        BeginAsm() 
        !"xor ebx, ebx;"
        !"mov eax, 0x1;"
        !"cpuid;"
        !"shr ebx, 24;"
        !"mov %[a], ebx;"
        !"rdtsc;" 
        !"mov %[t1], eax;" 
        AsmOutput2(a,t1)   
        !:
        AsmClobbers() "eax","ebx","edx"
        EndAsm()
        Delay(100) 
        BeginAsm() 
        !"xor ebx, ebx;"
        !"mov eax, 0x1;"
        !"cpuid;"
        !"shr ebx, 24;"
        !"mov %[b],ebx;" 
        !"rdtsc;" 
        !"mov %[t2],eax;" 
        AsmOutput2(b,t2)   
        !:
        AsmClobbers() "eax","ebx","edx"
        EndAsm()
      CompilerElse 
        !xor ebx, ebx
        !mov eax, 0x1
        !cpuid
        !shr ebx, 24
        !mov [p.v_a],ebx
        !rdtsc 
        !mov [p.v_t1],eax 
        Delay(100) 
        !xor ebx, ebx
        !mov eax, 0x1
        !cpuid
        !shr ebx, 24
        !mov [p.v_b],ebx 
        !rdtsc 
        !mov [p.v_t2],eax 
      CompilerEndIf  
      
      If a = b  
        result = ((t2-t1) / (100000))  
        result = ((result+5) / 5) * 5  
        If t2 - t1 > 0 
          Break  
        EndIf   
      EndIf    
    ForEver 
    
    ProcedureReturn result 
    
  EndProcedure  
  
  Procedure.s GetCPUSerialNumber()
    Define.l highbits, lowbits
    Define.q serial
    
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C  
      BeginAsm() 
      !"mov eax, 0x80000003;"
      !"cpuid;"
      !"mov %[lowbits],ecx;"
      !"mov %[highbits],edx;"
      AsmOutput2(lowbits,highbits) 
      !:
      AsmClobbers() "eax","ecx","edx" 
      EndAsm() 
    CompilerElse 
      !MOV eax, $80000003
      !CPUID
      !MOV dword [p.v_lowbits], ecx
      !MOV dword [p.v_highbits], edx
    CompilerEndIf 
    serial = lowbits | highbits << 32
    ProcedureReturn Hex(serial, #PB_Quad)
  EndProcedure
  
  
EndModule 

;Test code 
CompilerIf #PB_Compiler_IsMainFile 
  
  Prototype.s MyFunction()                
  Global MyFunction.MyFunction 
  
  Procedure.s _MyFunction()
    ProcedureReturn "fall back mode"
  EndProcedure   
  
  Procedure.s _MyFunction_SSE2()
    ProcedureReturn "SSE2 mode" 
  EndProcedure 
  
  Procedure InitDynamic() 
    UseModule CPUInfo
    If IsCPU("CPU_SSE2")
      MyFunction = @_MyFunction_SSE2()
    Else 
      MyFunction =@_MyFunction() 
    EndIf   
    UnuseModule CPUInfo 
  EndProcedure 
  
  Debug CPUInfo::GetVendor() 
  Debug CPUInfo::GetVendorVM()
  Debug CPUInfo::GetName()
  Debug CPUInfo::GetCPUSerialNumber() 
  Debug Str(CPUInfo::CountCores()) + " cores"
  Debug "Running on core number " + Str(CPUInfo::GetActiveCore()) 
  Debug "Running at " + Str(CPUInfo::GetFrequency()) +  " mhz" 
  Debug cpuinfo::GetAllFeatures() 
  
  InitDynamic()
  Debug MyFunction() 
  
CompilerEndIf 

Windows 11, Manjaro, Raspberry Pi OS
Image
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: cpuinfo module

Post by Kwai chang caine »

Hello IDLE :D
Works great, thanks for sharing 8)
1
1
1
1
1
1
0
0
Intel(R) Celeron(R) CPU 1005M @ 1.90GHz
GenuineIntel
2
ImageThe happiness is a road...
Not a destination
User avatar
takado
New User
New User
Posts: 7
Joined: Mon Jun 18, 2012 9:10 pm
Location: Germany
Contact:

Re: cpuinfo module

Post by takado »

Thanks for sharing, works good here at work (iMac):

Code: Select all

1
1
1
1
0
0
0
0
Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
GenuineIntel
2
PB 5.23LTS // Windows 7 // MacOsX 10.7.5 // Linux Mint 16
PureGuy
Enthusiast
Enthusiast
Posts: 102
Joined: Mon Aug 30, 2010 11:51 am

Re: cpuinfo module

Post by PureGuy »

Thanks, but I get wrong results for x64 compiled

x86 - PB 5.24
1
1
1
1
1
1
0
0
Intel(R) Core(TM) i5 CPU 750 @ 2.67GHz
GenuineIntel
4
x64 - PB 5.24
1
1
1
0
0
0
0
0
Intel(R) Core(TM) i5 CPU 750 @ 2.67GHz
GenuineIntel
4
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: cpuinfo module

Post by wilbert »

PureGuy wrote:Thanks, but I get wrong results for x64 compiled
On OS X it gives me the same results on both x86 and x64.
Does it make a difference if the entire IsCPU procedure is ASM instead of a part of it ?

Code: Select all

  Procedure IsCPU(op)
    !mov eax, 1
    !cpuid
    !xchg eax, ecx
    !mov ecx, [p.v_op]
    !shr edx, cl
    !shr eax, cl
    !sub ecx, 32
    !shr ecx, 31
    !and edx, ecx
    !xor ecx, 1
    !and eax, ecx
    !or eax, edx
    ProcedureReturn   
  EndProcedure
Windows (x64)
Raspberry Pi OS (Arm64)
PureGuy
Enthusiast
Enthusiast
Posts: 102
Joined: Mon Aug 30, 2010 11:51 am

Re: cpuinfo module

Post by PureGuy »

wilbert wrote: Does it make a difference if the entire IsCPU procedure is ASM instead of a part of it ?
Yes, using your asm procedure returns correct for both compiler :D
User avatar
idle
Always Here
Always Here
Posts: 5049
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: cpuinfo module

Post by idle »

updated the post with wilberts routine.
Windows 11, Manjaro, Raspberry Pi OS
Image
ozzie
Enthusiast
Enthusiast
Posts: 429
Joined: Sun Apr 06, 2008 12:54 pm
Location: Brisbane, Qld, Australia
Contact:

Re: cpuinfo module

Post by ozzie »

Thanks for sharing, idle. Useful info to include in the diagnostic file I sometimes need my users to create and send me.
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: cpuinfo module

Post by davido »

Thanks for sharing idle and wilbert.
Very useful. :D
DE AA EB
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: cpuinfo module

Post by Little John »

Previously, I encountered the same issue as PureGuy.
The new version works as expected.

Tested on Windows 7 with PB 5.31 x86 and x64,
and on Linux Mint 17 with PB 5.31 x86.

Thank you, idle and wilbert!
Opcode
Enthusiast
Enthusiast
Posts: 137
Joined: Thu Jul 18, 2013 4:58 am

Re: cpuinfo module

Post by Opcode »

Code: Select all

Debug IsCPU(#htt)
Returns 1 on my A10-6800k which obviously doesn't have Hyper-Threading. :shock:
User avatar
idle
Always Here
Always Here
Posts: 5049
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: cpuinfo module

Post by idle »

Opcode wrote:

Code: Select all

Debug IsCPU(#htt)
Returns 1 on my A10-6800k which obviously doesn't have Hyper-Threading. :shock:
I might need to implement the amd specific table.
Windows 11, Manjaro, Raspberry Pi OS
Image
Opcode
Enthusiast
Enthusiast
Posts: 137
Joined: Thu Jul 18, 2013 4:58 am

Re: cpuinfo module

Post by Opcode »

idle wrote:
Opcode wrote:

Code: Select all

Debug IsCPU(#htt)
Returns 1 on my A10-6800k which obviously doesn't have Hyper-Threading. :shock:
I might need to implement the amd specific table.
Not sure if AMD intends to do this to label modules as a form HTT.
User avatar
idle
Always Here
Always Here
Posts: 5049
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: cpuinfo module

Post by idle »

as far as I can tell the HHT bit is just indicating that there's more than one core
Windows 11, Manjaro, Raspberry Pi OS
Image
User avatar
idle
Always Here
Always Here
Posts: 5049
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: cpuinfo module

Post by idle »

Added GetActiveCore and GetFrequency
note GetFrequency isn't 100% accurate and it' rounded to the highest 5th
Windows 11, Manjaro, Raspberry Pi OS
Image
Post Reply