Skip to content

Commit 379a11b

Browse files
committed
improve SIMD detection support
1 parent b71fd85 commit 379a11b

22 files changed

Lines changed: 525 additions & 79 deletions

HashLib.Tests/Delphi.Tests/HashLib.Tests.dpr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ uses
162162
PBKDF2_HMACTests in '..\src\PBKDF2_HMACTests.pas',
163163
PBKDF_Argon2Tests in '..\src\PBKDF_Argon2Tests.pas',
164164
PBKDF_ScryptTests in '..\src\PBKDF_ScryptTests.pas',
165-
CRCTests in '..\src\CRCTests.pas';
165+
CRCTests in '..\src\CRCTests.pas',
166+
SimdSelectSlotTests in '..\src\SimdSelectSlotTests.pas';
166167

167168
begin
168169

HashLib.Tests/FreePascal.Tests/HashLib.Tests.lpi

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
<PackageName Value="FCL"/>
3939
</Item4>
4040
</RequiredPackages>
41-
<Units Count="14">
41+
<Units Count="15">
4242
<Unit0>
4343
<Filename Value="HashLib.lpr"/>
4444
<IsPartOfProject Value="True"/>
@@ -96,6 +96,10 @@
9696
<Filename Value="..\src\TestVectors.pas"/>
9797
<IsPartOfProject Value="True"/>
9898
</Unit13>
99+
<Unit14>
100+
<Filename Value="..\src\SimdSelectSlotTests.pas"/>
101+
<IsPartOfProject Value="True"/>
102+
</Unit14>
99103
</Units>
100104
</ProjectOptions>
101105
<CompilerOptions>

HashLib.Tests/FreePascal.Tests/HashLib.lpr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
PBKDF2_HMACTests,
1919
PBKDF_Argon2Tests,
2020
PBKDF_ScryptTests,
21-
CRCTests;
21+
CRCTests,
22+
SimdSelectSlotTests;
2223

2324
{$R *.res}
2425

HashLib.Tests/FreePascal.Tests/HashLibConsole.lpi

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
<PackageName Value="FCL"/>
6363
</Item2>
6464
</RequiredPackages>
65-
<Units Count="14">
65+
<Units Count="15">
6666
<Unit0>
6767
<Filename Value="HashLibConsole.lpr"/>
6868
<IsPartOfProject Value="True"/>
@@ -120,6 +120,10 @@
120120
<Filename Value="..\src\TestVectors.pas"/>
121121
<IsPartOfProject Value="True"/>
122122
</Unit13>
123+
<Unit14>
124+
<Filename Value="..\src\SimdSelectSlotTests.pas"/>
125+
<IsPartOfProject Value="True"/>
126+
</Unit14>
123127
</Units>
124128
</ProjectOptions>
125129
<CompilerOptions>

HashLib.Tests/FreePascal.Tests/HashLibConsole.lpr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
PBKDF2_HMACTests,
1818
PBKDF_Argon2Tests,
1919
PBKDF_ScryptTests,
20-
CRCTests;
20+
CRCTests,
21+
SimdSelectSlotTests;
2122

2223
type
2324

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
unit SimdSelectSlotTests;
2+
3+
interface
4+
5+
uses
6+
SysUtils,
7+
{$IFDEF FPC}
8+
fpcunit,
9+
testregistry,
10+
{$ELSE}
11+
TestFramework,
12+
{$ENDIF FPC}
13+
HlpSimdLevels,
14+
HlpX86SimdFeatures,
15+
HlpArmSimdFeatures;
16+
17+
type
18+
19+
THashLibTestCase = class abstract(TTestCase)
20+
21+
end;
22+
23+
type
24+
25+
// Exercises the pure overload of TX86SimdFeatures.SelectSlot, which
26+
// takes the active level as a parameter and is therefore fully
27+
// deterministic and host-CPU-independent.
28+
TTestX86SelectSlot = class(THashLibTestCase)
29+
published
30+
procedure TestExactMatch;
31+
procedure TestStepDownOnUnsupportedTier;
32+
procedure TestAllDeclaredTiersAboveActive;
33+
procedure TestEmptyTiers;
34+
procedure TestTierOrderIndependence;
35+
procedure TestScalarHost;
36+
procedure TestScalarTierAlwaysReachable;
37+
end;
38+
39+
type
40+
41+
// Symmetric coverage for the ARM surface, since the same SelectSlot
42+
// shape lives on TArmSimdFeatures.
43+
TTestArmSelectSlot = class(THashLibTestCase)
44+
published
45+
procedure TestExactMatch;
46+
procedure TestStepDownOnUnsupportedTier;
47+
procedure TestAllDeclaredTiersAboveActive;
48+
procedure TestEmptyTiers;
49+
procedure TestTierOrderIndependence;
50+
procedure TestScalarHost;
51+
end;
52+
53+
implementation
54+
55+
function X86LevelName(ALevel: TX86SimdLevel): string;
56+
begin
57+
case ALevel of
58+
TX86SimdLevel.Scalar: Result := 'Scalar';
59+
TX86SimdLevel.SSE2: Result := 'SSE2';
60+
TX86SimdLevel.SSE3: Result := 'SSE3';
61+
TX86SimdLevel.SSSE3: Result := 'SSSE3';
62+
TX86SimdLevel.SSE41: Result := 'SSE41';
63+
TX86SimdLevel.SSE42: Result := 'SSE42';
64+
TX86SimdLevel.AVX2: Result := 'AVX2';
65+
else
66+
Result := 'Unknown';
67+
end;
68+
end;
69+
70+
function ArmLevelName(ALevel: TArmSimdLevel): string;
71+
begin
72+
case ALevel of
73+
TArmSimdLevel.Scalar: Result := 'Scalar';
74+
TArmSimdLevel.NEON: Result := 'NEON';
75+
TArmSimdLevel.SVE: Result := 'SVE';
76+
TArmSimdLevel.SVE2: Result := 'SVE2';
77+
else
78+
Result := 'Unknown';
79+
end;
80+
end;
81+
82+
{ TTestX86SelectSlot }
83+
84+
procedure TTestX86SelectSlot.TestExactMatch;
85+
var
86+
LResult: TX86SimdLevel;
87+
begin
88+
// Active host advertises AVX2; AVX2 is declared, so it should win.
89+
LResult := TX86SimdFeatures.SelectSlot(TX86SimdLevel.AVX2,
90+
[TX86SimdLevel.AVX2, TX86SimdLevel.SSE2]);
91+
CheckTrue(LResult = TX86SimdLevel.AVX2,
92+
Format('Expected AVX2 but got %s.', [X86LevelName(LResult)]));
93+
end;
94+
95+
procedure TTestX86SelectSlot.TestStepDownOnUnsupportedTier;
96+
var
97+
LResult: TX86SimdLevel;
98+
begin
99+
// Host advertises SSE41 (probed but not declared by the algorithm).
100+
// The algorithm declares only AVX2 and SSE2; SelectSlot must step
101+
// down to SSE2 (the central future-proofing claim).
102+
LResult := TX86SimdFeatures.SelectSlot(TX86SimdLevel.SSE41,
103+
[TX86SimdLevel.AVX2, TX86SimdLevel.SSE2]);
104+
CheckTrue(LResult = TX86SimdLevel.SSE2,
105+
Format('Expected SSE2 but got %s.', [X86LevelName(LResult)]));
106+
end;
107+
108+
procedure TTestX86SelectSlot.TestAllDeclaredTiersAboveActive;
109+
var
110+
LResult: TX86SimdLevel;
111+
begin
112+
// Host caps at SSE2; algorithm declares only AVX2. No tier matches,
113+
// so SelectSlot must fall back to Scalar.
114+
LResult := TX86SimdFeatures.SelectSlot(TX86SimdLevel.SSE2,
115+
[TX86SimdLevel.AVX2]);
116+
CheckTrue(LResult = TX86SimdLevel.Scalar,
117+
Format('Expected Scalar but got %s.', [X86LevelName(LResult)]));
118+
end;
119+
120+
procedure TTestX86SelectSlot.TestEmptyTiers;
121+
var
122+
LResult: TX86SimdLevel;
123+
LEmpty: array of TX86SimdLevel;
124+
begin
125+
// An algorithm with no SIMD impls passes an empty tier array.
126+
// SelectSlot must fall back to Scalar regardless of host capability.
127+
System.SetLength(LEmpty, 0);
128+
LResult := TX86SimdFeatures.SelectSlot(TX86SimdLevel.AVX2, LEmpty);
129+
CheckTrue(LResult = TX86SimdLevel.Scalar,
130+
Format('Expected Scalar but got %s.', [X86LevelName(LResult)]));
131+
end;
132+
133+
procedure TTestX86SelectSlot.TestTierOrderIndependence;
134+
var
135+
LDescending, LAscending: TX86SimdLevel;
136+
begin
137+
// SelectSlot reasons over the set of declared tiers, not their order.
138+
// Both orderings must yield the same result for the same host level.
139+
LDescending := TX86SimdFeatures.SelectSlot(TX86SimdLevel.SSE42,
140+
[TX86SimdLevel.AVX2, TX86SimdLevel.SSE2]);
141+
LAscending := TX86SimdFeatures.SelectSlot(TX86SimdLevel.SSE42,
142+
[TX86SimdLevel.SSE2, TX86SimdLevel.AVX2]);
143+
CheckTrue(LDescending = LAscending,
144+
Format('Order-dependent result: descending=%s ascending=%s.',
145+
[X86LevelName(LDescending), X86LevelName(LAscending)]));
146+
CheckTrue(LDescending = TX86SimdLevel.SSE2,
147+
Format('Expected SSE2 but got %s.', [X86LevelName(LDescending)]));
148+
end;
149+
150+
procedure TTestX86SelectSlot.TestScalarHost;
151+
var
152+
LResult: TX86SimdLevel;
153+
begin
154+
// A host that probed Scalar must never select any SIMD tier.
155+
LResult := TX86SimdFeatures.SelectSlot(TX86SimdLevel.Scalar,
156+
[TX86SimdLevel.AVX2, TX86SimdLevel.SSE2]);
157+
CheckTrue(LResult = TX86SimdLevel.Scalar,
158+
Format('Expected Scalar but got %s.', [X86LevelName(LResult)]));
159+
end;
160+
161+
procedure TTestX86SelectSlot.TestScalarTierAlwaysReachable;
162+
var
163+
LResult: TX86SimdLevel;
164+
begin
165+
// If the algorithm explicitly declares Scalar as a tier, that is
166+
// always reachable - even on a Scalar host.
167+
LResult := TX86SimdFeatures.SelectSlot(TX86SimdLevel.Scalar,
168+
[TX86SimdLevel.Scalar]);
169+
CheckTrue(LResult = TX86SimdLevel.Scalar,
170+
Format('Expected Scalar but got %s.', [X86LevelName(LResult)]));
171+
end;
172+
173+
{ TTestArmSelectSlot }
174+
175+
procedure TTestArmSelectSlot.TestExactMatch;
176+
var
177+
LResult: TArmSimdLevel;
178+
begin
179+
LResult := TArmSimdFeatures.SelectSlot(TArmSimdLevel.SVE2,
180+
[TArmSimdLevel.SVE2, TArmSimdLevel.NEON]);
181+
CheckTrue(LResult = TArmSimdLevel.SVE2,
182+
Format('Expected SVE2 but got %s.', [ArmLevelName(LResult)]));
183+
end;
184+
185+
procedure TTestArmSelectSlot.TestStepDownOnUnsupportedTier;
186+
var
187+
LResult: TArmSimdLevel;
188+
begin
189+
// Host advertises SVE; algorithm offers SVE2 + NEON only.
190+
// Must step down to NEON (highest reachable declared tier).
191+
LResult := TArmSimdFeatures.SelectSlot(TArmSimdLevel.SVE,
192+
[TArmSimdLevel.SVE2, TArmSimdLevel.NEON]);
193+
CheckTrue(LResult = TArmSimdLevel.NEON,
194+
Format('Expected NEON but got %s.', [ArmLevelName(LResult)]));
195+
end;
196+
197+
procedure TTestArmSelectSlot.TestAllDeclaredTiersAboveActive;
198+
var
199+
LResult: TArmSimdLevel;
200+
begin
201+
LResult := TArmSimdFeatures.SelectSlot(TArmSimdLevel.NEON,
202+
[TArmSimdLevel.SVE2]);
203+
CheckTrue(LResult = TArmSimdLevel.Scalar,
204+
Format('Expected Scalar but got %s.', [ArmLevelName(LResult)]));
205+
end;
206+
207+
procedure TTestArmSelectSlot.TestEmptyTiers;
208+
var
209+
LResult: TArmSimdLevel;
210+
LEmpty: array of TArmSimdLevel;
211+
begin
212+
System.SetLength(LEmpty, 0);
213+
LResult := TArmSimdFeatures.SelectSlot(TArmSimdLevel.SVE2, LEmpty);
214+
CheckTrue(LResult = TArmSimdLevel.Scalar,
215+
Format('Expected Scalar but got %s.', [ArmLevelName(LResult)]));
216+
end;
217+
218+
procedure TTestArmSelectSlot.TestTierOrderIndependence;
219+
var
220+
LDescending, LAscending: TArmSimdLevel;
221+
begin
222+
LDescending := TArmSimdFeatures.SelectSlot(TArmSimdLevel.SVE,
223+
[TArmSimdLevel.SVE2, TArmSimdLevel.NEON]);
224+
LAscending := TArmSimdFeatures.SelectSlot(TArmSimdLevel.SVE,
225+
[TArmSimdLevel.NEON, TArmSimdLevel.SVE2]);
226+
CheckTrue(LDescending = LAscending,
227+
Format('Order-dependent result: descending=%s ascending=%s.',
228+
[ArmLevelName(LDescending), ArmLevelName(LAscending)]));
229+
CheckTrue(LDescending = TArmSimdLevel.NEON,
230+
Format('Expected NEON but got %s.', [ArmLevelName(LDescending)]));
231+
end;
232+
233+
procedure TTestArmSelectSlot.TestScalarHost;
234+
var
235+
LResult: TArmSimdLevel;
236+
begin
237+
LResult := TArmSimdFeatures.SelectSlot(TArmSimdLevel.Scalar,
238+
[TArmSimdLevel.SVE2, TArmSimdLevel.NEON]);
239+
CheckTrue(LResult = TArmSimdLevel.Scalar,
240+
Format('Expected Scalar but got %s.', [ArmLevelName(LResult)]));
241+
end;
242+
243+
initialization
244+
245+
{$IFDEF FPC}
246+
RegisterTest(TTestX86SelectSlot);
247+
RegisterTest(TTestArmSelectSlot);
248+
{$ELSE}
249+
RegisterTest(TTestX86SelectSlot.Suite);
250+
RegisterTest(TTestArmSelectSlot.Suite);
251+
{$ENDIF FPC}
252+
253+
end.

HashLib/src/Checksum/HlpAdler32Dispatch.pas

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ procedure InitDispatch();
189189
begin
190190
Adler32_Update := @Adler32_Update_Scalar;
191191
{$IFDEF HASHLIB_I386_ASM}
192-
case TCpuFeatures.X86.GetActiveSimdLevel() of
192+
case TCpuFeatures.X86.SelectSlot([TX86SimdLevel.SSSE3, TX86SimdLevel.SSE2]) of
193193
TX86SimdLevel.SSSE3:
194194
begin
195195
Adler32_Update := @Adler32_Update_Ssse3;
@@ -201,7 +201,7 @@ procedure InitDispatch();
201201
end;
202202
{$ENDIF}
203203
{$IFDEF HASHLIB_X86_64_ASM}
204-
case TCpuFeatures.X86.GetActiveSimdLevel() of
204+
case TCpuFeatures.X86.SelectSlot([TX86SimdLevel.AVX2, TX86SimdLevel.SSSE3, TX86SimdLevel.SSE2]) of
205205
TX86SimdLevel.AVX2:
206206
begin
207207
Adler32_Update := @Adler32_Update_Avx2;

HashLib/src/Checksum/HlpCRCDispatch.pas

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -514,18 +514,10 @@ procedure InitDispatch();
514514
{$ENDIF HASHLIB_X86_64_ASM}
515515

516516
{$IFDEF HASHLIB_X86_SIMD}
517-
{$IFDEF HASHLIB_I386_ASM}
518-
case TCpuFeatures.X86.GetActiveSimdLevel() of
519-
TX86SimdLevel.SSSE3, TX86SimdLevel.SSE2:
517+
case TCpuFeatures.X86.SelectSlot([TX86SimdLevel.SSE2]) of
518+
TX86SimdLevel.SSE2:
520519
BindSse2CrcFold;
521520
end;
522-
{$ENDIF HASHLIB_I386_ASM}
523-
{$IFDEF HASHLIB_X86_64_ASM}
524-
case TCpuFeatures.X86.GetActiveSimdLevel() of
525-
TX86SimdLevel.AVX2, TX86SimdLevel.SSSE3, TX86SimdLevel.SSE2:
526-
BindSse2CrcFold;
527-
end;
528-
{$ENDIF HASHLIB_X86_64_ASM}
529521
{$ENDIF HASHLIB_X86_SIMD}
530522
end;
531523

HashLib/src/Crypto/HlpBlake2BDispatch.pas

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,20 +132,20 @@ procedure InitDispatch();
132132
begin
133133
Blake2B_Compress := @Blake2B_Compress_Scalar;
134134
{$IFDEF HASHLIB_I386_ASM}
135-
case TCpuFeatures.X86.GetActiveSimdLevel() of
136-
TX86SimdLevel.SSE2, TX86SimdLevel.SSSE3:
135+
case TCpuFeatures.X86.SelectSlot([TX86SimdLevel.SSE2]) of
136+
TX86SimdLevel.SSE2:
137137
begin
138138
Blake2B_Compress := @Blake2B_Compress_Sse2;
139139
end;
140140
end;
141141
{$ENDIF}
142142
{$IFDEF HASHLIB_X86_64_ASM}
143-
case TCpuFeatures.X86.GetActiveSimdLevel() of
143+
case TCpuFeatures.X86.SelectSlot([TX86SimdLevel.AVX2, TX86SimdLevel.SSE2]) of
144144
TX86SimdLevel.AVX2:
145145
begin
146146
Blake2B_Compress := @Blake2B_Compress_Avx2;
147147
end;
148-
TX86SimdLevel.SSE2, TX86SimdLevel.SSSE3:
148+
TX86SimdLevel.SSE2:
149149
begin
150150
Blake2B_Compress := @Blake2B_Compress_Sse2;
151151
end;

0 commit comments

Comments
 (0)