How to probe a computer if it supports SSE2 in Delphi 32?
Asked Answered
V

3

6

The c++ way to do it is here (under Windows).

The same answer but under Linux using GCC.

Excerpt of the relevant asm code as I understand it:

mov     eax, 1
cpuid
mov     features, edx

I'm not very comfortable at BASM.

My Question:

I need to wrap the test as follows

function IsSSE2: Boolean;
begin
  try
    Result := False;
    //
    // Some BASM code here
    //
  except
    Result := False;
  end;
end;

Please help me.

Volsung answered 18/2, 2012 at 12:24 Comment(2)
Be careful. Both the CPU and OS must support SSE2. The OS must support it because the SSE registers get saved to memory on a context switch, and the OS has to supply the area of memory. That's why its sometimes not enough to test for the SSE2 cpu feature bit. That's also why you see tests for XSTORE and FXSAVE support. IIRC, they are enabled if the OS supplies the memory area; otherwise the OS disables it (some hand waiving). Its usually not a problem nowadays unless you support older processors and OSes. Also see Section 11.6.2, Checking for SSE/SSE2 Support in the Intel Programmers Manual.Isopleth
Also see Determine processor support for SSE2? and How to check if a CPU supports the SSE3 instruction set?. The second question provides details of OS support.Isopleth
D
21

You can do that without assembler as well. Works with Windows XP and newer only though.

function IsProcessorFeaturePresent(ProcessorFeature: DWORD): BOOL; stdcall;
  external kernel32 name 'IsProcessorFeaturePresent';

const
  PF_XMMI64_INSTRUCTIONS_AVAILABLE = 10;

function HasSSE2: boolean;
begin
  result := IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
end;
Deviant answered 18/2, 2012 at 13:19 Comment(4)
+1. This seems like the best answer (unless you need to support Win 2000).Canker
I love delegating these jobs. Prefer this approach.Siloa
+1 clearly the best solution if you can ignore windows 2000 and other platformsKimono
+1 and it may also work with Delphi XE2 targeting Win64 - whereas the asm version will fail to work (bound to 32 bit asm yet).Halve
C
8

I think that this does it:

function SupportsSSE2: LongBool;
const
  CPUID_INTEL_SSE2 = $04000000;
asm
  push ebx
  mov eax, 1
  cpuid
  mov eax, FALSE
  test edx, CPUID_INTEL_SSE2
  jz @END
  mov eax, TRUE
@END:
  pop ebx
end;
Canker answered 18/2, 2012 at 12:32 Comment(1)
Can I please safely change the function signature to function SupportsSSE2: Boolean;?Volsung
K
7

Here is the code used by the graphics32 library to detect processor features:

{$IFDEF WIN64}
  {$DEFINE TARGET_x64}
{$ENDIF}

type
  TCPUInstructionSet = (ciMMX, ciEMMX, ciSSE, ciSSE2, ci3DNow, ci3DNowExt);

const
  CPUISChecks: Array[TCPUInstructionSet] of Cardinal =
    ($800000,  $400000, $2000000, $4000000, $80000000, $40000000);
    {ciMMX  ,  ciEMMX,  ciSSE   , ciSSE2  , ci3DNow ,  ci3DNowExt}

function CPUID_Available: Boolean;
asm
{$IFDEF TARGET_x64}
        MOV       EDX,False
        PUSHFQ
        POP       RAX
        MOV       ECX,EAX
        XOR       EAX,$00200000
        PUSH      RAX
        POPFQ
        PUSHFQ
        POP       RAX
        XOR       ECX,EAX
        JZ        @1
        MOV       EDX,True
@1:     PUSH      RAX
        POPFQ
        MOV       EAX,EDX
{$ELSE}
        MOV       EDX,False
        PUSHFD
        POP       EAX
        MOV       ECX,EAX
        XOR       EAX,$00200000
        PUSH      EAX
        POPFD
        PUSHFD
        POP       EAX
        XOR       ECX,EAX
        JZ        @1
        MOV       EDX,True
@1:     PUSH      EAX
        POPFD
        MOV       EAX,EDX
{$ENDIF}
end;

function CPU_Signature: Integer;
asm
{$IFDEF TARGET_x64}
        PUSH      RBX
        MOV       EAX,1
        CPUID
        POP       RBX
{$ELSE}
        PUSH      EBX
        MOV       EAX,1
        {$IFDEF FPC}
        CPUID
        {$ELSE}
        DW        $A20F   // CPUID
        {$ENDIF}
        POP       EBX
{$ENDIF}
end;

function CPU_Features: Integer;
asm
{$IFDEF TARGET_x64}
        PUSH      RBX
        MOV       EAX,1
        CPUID
        POP       RBX
        MOV       EAX,EDX
{$ELSE}
        PUSH      EBX
        MOV       EAX,1
        {$IFDEF FPC}
        CPUID
        {$ELSE}
        DW        $A20F   // CPUID
        {$ENDIF}
        POP       EBX
        MOV       EAX,EDX
{$ENDIF}
end;

function CPU_ExtensionsAvailable: Boolean;
asm
{$IFDEF TARGET_x64}
        PUSH      RBX
        MOV       @Result, True
        MOV       EAX, $80000000
        CPUID
        CMP       EAX, $80000000
        JBE       @NOEXTENSION
        JMP       @EXIT
        @NOEXTENSION:
        MOV       @Result, False
        @EXIT:
        POP       RBX
{$ELSE}
        PUSH      EBX
        MOV       @Result, True
        MOV       EAX, $80000000
        {$IFDEF FPC}
        CPUID
        {$ELSE}
        DW        $A20F   // CPUID
        {$ENDIF}
        CMP       EAX, $80000000
        JBE       @NOEXTENSION
        JMP       @EXIT
      @NOEXTENSION:
        MOV       @Result, False
      @EXIT:
        POP       EBX
{$ENDIF}
end;

function CPU_ExtFeatures: Integer;
asm
{$IFDEF TARGET_x64}
        PUSH      RBX
        MOV       EAX, $80000001
        CPUID
        POP       RBX
        MOV       EAX,EDX
{$ELSE}
        PUSH      EBX
        MOV       EAX, $80000001
        {$IFDEF FPC}
        CPUID
        {$ELSE}
        DW        $A20F   // CPUID
        {$ENDIF}
        POP       EBX
        MOV       EAX,EDX
{$ENDIF}
end;

function HasInstructionSet(const InstructionSet: TCPUInstructionSet): Boolean;
// Must be implemented for each target CPU on which specific functions rely
begin
  Result := False;
  if not CPUID_Available then Exit;                   // no CPUID available
  if CPU_Signature shr 8 and $0F < 5 then Exit;       // not a Pentium class

  case InstructionSet of
    ci3DNow, ci3DNowExt:
      {$IFNDEF FPC}
      if not CPU_ExtensionsAvailable or (CPU_ExtFeatures and CPUISChecks[InstructionSet] = 0) then
      {$ENDIF}
        Exit;
    ciEMMX:
      begin
        // check for SSE, necessary for Intel CPUs because they don't implement the
        // extended info
        if (CPU_Features and CPUISChecks[ciSSE] = 0) and
          (not CPU_ExtensionsAvailable or (CPU_ExtFeatures and CPUISChecks[ciEMMX] = 0)) then
          Exit;
      end;
  else
    if CPU_Features and CPUISChecks[InstructionSet] = 0 then
      Exit; // return -> instruction set not supported
    end;

  Result := True;
end;

You can call HasInstructionSet(ciSSE2) to discover what you need.

Kimono answered 18/2, 2012 at 12:39 Comment(3)
Thank you David! It never crosses my mind that I can readily have it in Graphic32.Volsung
It was at the front of my mind because I'm doing a 64 bit port at the moment and had some problems with unaligned memory and SSE2 instructions under 64 bit!Kimono
This could pose a problem on Cyrix/NextGen chips where ID has to be left set to leave CPUID facility in the "enabled" state.Corinthian

© 2022 - 2024 — McMap. All rights reserved.