如何在Delphi 32中探测计算机是否支持SSE2?

6

在Windows下,使用c++的方法可以参考这里

在Linux下,使用GCC的方法可以参考同样的答案

以下是相关汇编代码的摘录:

mov     eax, 1
cpuid
mov     features, edx

我很不舒服BASM。

我的问题:

我需要将文本包装如下:

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

请帮助我。


请注意,CPU和操作系统都必须支持SSE2。操作系统必须支持它,因为在上下文切换时,SSE寄存器会保存到内存中,而操作系统必须提供内存区域。这就是为什么有时仅测试SSE2 CPU特性位不足够的原因。这也是为什么您会看到对XSTORE和FXSAVE支持的测试。如果操作系统提供内存区域,则启用它们;否则,操作系统将禁用它们(有些含糊)。现在通常不是问题,除非您支持旧处理器和操作系统。另请参阅英特尔程序员手册中的“第11.6.2节,检查SSE / SSE2支持”。 - jww
还可以参考Determine processor support for SSE2?How to check if a CPU supports the SSE3 instruction set?。第二个问题提供了操作系统支持的详细信息。 - jww
3个回答

21

你也可以不使用汇编来实现这个功能。但是这种方法只适用于Windows XP及更高版本。

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;

2
+1. 这似乎是最好的答案(除非您需要支持Win 2000)。 - Andreas Rejbrand
我喜欢委派这些工作。更喜欢这种方法。 - Warren P
1
+1 明显是最佳解决方案,如果您可以忽略 Windows 2000 和其他平台。 - David Heffernan
+1,它也可能与针对Win64的Delphi XE2配合使用 - 而asm版本将无法工作(只绑定32位asm)。 - Arnaud Bouchez

8
我认为这样做就可以了:
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;

我能否安全地将函数签名更改为 function SupportsSSE2: Boolean; - menjaraz

7

这是graphics32库用来检测处理器特性的代码:

{$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;

您可以调用HasInstructionSet(ciSSE2)来发现您所需的内容。


谢谢你,David!我从未想过我可以轻松地在Graphic32中使用它。 - menjaraz
这个问题一直在我脑海中,因为我正在进行64位端口工作,在64位下遇到了一些未对齐内存和SSE2指令的问题! - David Heffernan
这可能会在Cyrix/NextGen芯片上造成问题,因为ID必须保留设置以使CPUID功能处于“启用”状态。 - OnTheFly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接