Skip to content

Commit 84f62d0

Browse files
authored
Print hex format for non buffer-exact comparison rules for float types (#1019)
This PR fixes the output format for float types, that show up when the comparison rule is not buffer exact. It simplifies the Hex format output infrastructure that accompanies any mismatch result. Fixes #1018 Assisted by: Claude Opus 4.6
1 parent b7090ee commit 84f62d0

5 files changed

Lines changed: 152 additions & 84 deletions

File tree

lib/Support/Check.cpp

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
#include "Support/Pipeline.h"
1414
#include "llvm/ADT/APFloat.h"
1515
#include "llvm/ADT/APInt.h"
16+
#include "llvm/ADT/bit.h"
1617
#include "llvm/Support/Error.h"
1718
#include "llvm/Support/raw_ostream.h"
1819
#include <cmath>
20+
#include <cstring>
1921
#include <sstream>
2022

2123
constexpr uint16_t Float16BitSign = 0x8000;
@@ -280,33 +282,33 @@ static bool testBufferFloatULP(offloadtest::Buffer *B1, offloadtest::Buffer *B2,
280282
return false;
281283
}
282284

283-
template <typename T>
284-
static std::string bitPatternAsHex64(const T &Val,
285-
offloadtest::Rule ComparisonRule) {
285+
template <typename T> static uint64_t toBitPattern(const T &Val) {
286286
static_assert(sizeof(T) <= sizeof(uint64_t), "Type too large for Hex64");
287+
uint64_t Bits = 0;
288+
memcpy(&Bits, &Val, sizeof(T));
289+
return Bits;
290+
}
287291

292+
template <typename T> static std::string formatAsHex(const T &Val) {
288293
std::ostringstream Oss;
289-
if (ComparisonRule == offloadtest::Rule::BufferExact)
290-
Oss << "0x" << std::hex << Val;
291-
else
292-
Oss << std::hexfloat << Val;
294+
Oss << "0x" << std::hex << toBitPattern(Val);
293295
return Oss.str();
294296
}
295297

296298
template <typename T>
297-
static void formatBuffer(llvm::ArrayRef<T> Arr, offloadtest::Rule Rule,
299+
static void formatBuffer(llvm::ArrayRef<T> Arr,
298300
llvm::raw_svector_ostream &Result) {
299301
if (Arr.empty())
300302
return;
301303

302-
Result << "[ " << bitPatternAsHex64(Arr[0], Rule);
304+
Result << "[ " << formatAsHex(Arr[0]);
303305
for (size_t I = 1; I < Arr.size(); ++I)
304-
Result << ", " << bitPatternAsHex64(Arr[I], Rule);
306+
Result << ", " << formatAsHex(Arr[I]);
305307
Result << " ]";
306308
}
307309

308310
template <typename T>
309-
static void formatBufferArray(offloadtest::Buffer *B, offloadtest::Rule Rule,
311+
static void formatBufferArray(offloadtest::Buffer *B,
310312
llvm::raw_svector_ostream &Result) {
311313
assert(B->ArraySize > 1 && "Buffer must be an array to format as array");
312314
for (const auto &DataPtr : B->Data) {
@@ -315,62 +317,57 @@ static void formatBufferArray(offloadtest::Buffer *B, offloadtest::Rule Rule,
315317
Result << " - ";
316318
formatBuffer(llvm::ArrayRef<T>(reinterpret_cast<T *>(DataPtr.get()),
317319
B->Size / sizeof(T)),
318-
Rule, Result);
320+
Result);
319321
}
320322
}
321323

322-
template <typename T>
323-
static std::string formatBuffer(offloadtest::Buffer *B,
324-
offloadtest::Rule Rule) {
324+
template <typename T> static std::string formatBuffer(offloadtest::Buffer *B) {
325325
llvm::SmallString<256> Str;
326326
llvm::raw_svector_ostream Result(Str);
327327

328328
if (B->ArraySize > 1)
329-
formatBufferArray<T>(B, Rule, Result);
329+
formatBufferArray<T>(B, Result);
330330
else
331331
formatBuffer(llvm::ArrayRef<T>(reinterpret_cast<T *>(B->Data.back().get()),
332332
B->Size / sizeof(T)),
333-
Rule, Result);
333+
Result);
334334

335335
return std::string(Result.str());
336336
}
337337

338-
static const std::string getBufferStr(offloadtest::Buffer *B,
339-
offloadtest::Rule Rule) {
338+
static const std::string getBufferStr(offloadtest::Buffer *B) {
340339
using DF = offloadtest::DataFormat;
341340
switch (B->Format) {
342341
case DF::Hex8:
343-
return formatBuffer<llvm::yaml::Hex8>(B, Rule);
342+
return formatBuffer<llvm::yaml::Hex8>(B);
344343
case DF::Hex16:
345-
return formatBuffer<llvm::yaml::Hex16>(B, Rule);
344+
return formatBuffer<llvm::yaml::Hex16>(B);
346345
case DF::Hex32:
347-
return formatBuffer<llvm::yaml::Hex32>(B, Rule);
346+
return formatBuffer<llvm::yaml::Hex32>(B);
348347
case DF::Hex64:
349-
return formatBuffer<llvm::yaml::Hex64>(B, Rule);
348+
return formatBuffer<llvm::yaml::Hex64>(B);
350349
case DF::UInt16:
351-
return formatBuffer<uint16_t>(B, Rule);
350+
return formatBuffer<uint16_t>(B);
352351
case DF::UInt32:
353-
return formatBuffer<uint32_t>(B, Rule);
352+
return formatBuffer<uint32_t>(B);
354353
case DF::UInt64:
355-
return formatBuffer<uint64_t>(B, Rule);
354+
return formatBuffer<uint64_t>(B);
356355
case DF::Int16:
357-
return formatBuffer<int16_t>(B, Rule);
356+
return formatBuffer<int16_t>(B);
358357
case DF::Int32:
359-
return formatBuffer<int32_t>(B, Rule);
358+
return formatBuffer<int32_t>(B);
360359
case DF::Int64:
361-
return formatBuffer<int64_t>(B, Rule);
360+
return formatBuffer<int64_t>(B);
362361
case DF::Float16:
363-
return formatBuffer<llvm::yaml::Hex16>(B,
364-
Rule); // assuming no native float16
362+
return formatBuffer<llvm::yaml::Hex16>(B); // assuming no native float16
365363
case DF::Float32:
366364
case DF::Depth32:
367-
return formatBuffer<float>(B, Rule);
365+
return formatBuffer<float>(B);
368366
case DF::Float64:
369-
return formatBuffer<double>(B, Rule);
367+
return formatBuffer<double>(B);
370368
case DF::Bool:
371-
return formatBuffer<uint32_t>(B,
372-
Rule); // Because sizeof(bool) is 1 but HLSL
373-
// represents a bool using 4 bytes.
369+
return formatBuffer<uint32_t>(B); // Because sizeof(bool) is 1 but HLSL
370+
// represents a bool using 4 bytes.
374371
}
375372
}
376373

@@ -409,18 +406,20 @@ llvm::Error verifyResult(offloadtest::Result R) {
409406
OS << "Got:\n";
410407
YAMLOS << *R.ActualPtr;
411408

412-
// Now print exact hex64 representations of each element of the
409+
// Now print exact hex representations of each element of the
413410
// actual and expected buffers.
414411

415-
const std::string ExpectedBufferStr =
416-
getBufferStr(R.ExpectedPtr, R.ComparisonRule);
417-
const std::string ActualBufferStr =
418-
getBufferStr(R.ActualPtr, R.ComparisonRule);
412+
if constexpr (llvm::endianness::native == llvm::endianness::little) {
413+
const std::string ExpectedBufferStr = getBufferStr(R.ExpectedPtr);
414+
const std::string ActualBufferStr = getBufferStr(R.ActualPtr);
419415

420-
OS << "Full Hex 64bit representation of Expected Buffer Values:\n"
421-
<< ExpectedBufferStr << "\n";
422-
OS << "Full Hex 64bit representation of Actual Buffer Values:\n"
423-
<< ActualBufferStr << "\n";
416+
OS << "Full Hex representation of Expected Buffer Values:\n"
417+
<< ExpectedBufferStr << "\n";
418+
OS << "Full Hex representation of Actual Buffer Values:\n"
419+
<< ActualBufferStr << "\n";
420+
} else {
421+
OS << "Hex output is not supported on big-endian hosts.\n";
422+
}
424423

425424
return llvm::createStringError(Str.c_str());
426425
}

test/Tools/Offloader/BufferExact-error-array.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ DescriptorSets:
8484
# CHECK: Width: 0
8585
# CHECK: Depth: 0
8686
# CHECK: ...
87-
# CHECK: Full Hex 64bit representation of Expected Buffer Values:
87+
# CHECK: Full Hex representation of Expected Buffer Values:
8888
# CHECK: - [ 0x1, 0x2, 0x3, 0x4 ]
8989
# CHECK: - [ 0x5, 0x6, 0x7, 0x8 ]
90-
# CHECK: Full Hex 64bit representation of Actual Buffer Values:
90+
# CHECK: Full Hex representation of Actual Buffer Values:
9191
# CHECK: - [ 0x0, 0xa, 0x14, 0x1e ]
9292
# CHECK: - [ 0x1, 0xb, 0x15, 0x1f ]

test/Tools/Offloader/BufferExact-error.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ DescriptorSets:
7070
# CHECK: Height: 0
7171
# CHECK: Width: 0
7272
# CHECK: Depth: 0
73-
# CHECK: Full Hex 64bit representation of Expected Buffer Values:
73+
# CHECK: Full Hex representation of Expected Buffer Values:
7474
# CHECK: [ 0x1, 0x2, 0x3, 0x4 ]
75-
# CHECK: Full Hex 64bit representation of Actual Buffer Values:
75+
# CHECK: Full Hex representation of Actual Buffer Values:
7676
# CHECK: [ 0x14, 0x1e, 0x28, 0x32 ]
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#--- source.hlsl
2+
3+
RWStructuredBuffer<float> Out1 : register(u0);
4+
5+
[numthreads(1,1,1)]
6+
void main() {
7+
Out1[0] = 20.25;
8+
Out1[1] = 5.0;
9+
}
10+
11+
//--- pipeline.yaml
12+
13+
---
14+
Shaders:
15+
- Stage: Compute
16+
Entry: main
17+
DispatchSize: [1, 1, 1]
18+
Buffers:
19+
- Name: Out1
20+
Format: Float32
21+
Stride: 4
22+
FillSize: 8
23+
- Name: Expected1
24+
Format: Float32
25+
Stride: 4
26+
Data: [ 1.5, 2.5 ]
27+
Results:
28+
- Result: Test1
29+
Rule: BufferFloatULP
30+
ULPT: 1
31+
Actual: Out1
32+
Expected: Expected1
33+
DescriptorSets:
34+
- Resources:
35+
- Name: Out1
36+
Kind: RWStructuredBuffer
37+
DirectXBinding:
38+
Register: 0
39+
Space: 0
40+
VulkanBinding:
41+
Binding: 0
42+
...
43+
#--- end
44+
45+
# RUN: split-file %s %t
46+
# RUN: %dxc_target -T cs_6_5 -Fo %t.o %t/source.hlsl
47+
# RUN: not %offloader %t/pipeline.yaml %t.o 2>&1 | FileCheck %s
48+
49+
# CHECK: Test failed: Test1
50+
# CHECK: Comparison Rule: BufferFloatULP
51+
# CHECK: ULP: 1
52+
# CHECK: Expected:
53+
# CHECK: ---
54+
# CHECK: Name: Expected1
55+
# CHECK: Format: Float32
56+
# CHECK: Stride: 4
57+
# CHECK: Data: [ 1.5, 2.5 ]
58+
# CHECK: OutputProps:
59+
# CHECK: Height: 0
60+
# CHECK: Width: 0
61+
# CHECK: Depth: 0
62+
# CHECK: ...
63+
# CHECK: Got:
64+
# CHECK: ---
65+
# CHECK: Name: Out1
66+
# CHECK: Format: Float32
67+
# CHECK: Stride: 4
68+
# CHECK: Data: [ 20.25, 5 ]
69+
# CHECK: OutputProps:
70+
# CHECK: Height: 0
71+
# CHECK: Width: 0
72+
# CHECK: Depth: 0
73+
# CHECK: ...
74+
# CHECK-NEXT: Full Hex representation of Expected Buffer Values:
75+
# CHECK-NEXT: [ 0x3fc00000, 0x40200000 ]
76+
# CHECK-NEXT: Full Hex representation of Actual Buffer Values:
77+
# CHECK-NEXT: [ 0x41a20000, 0x40a00000 ]

test/Tools/Offloader/BufferFloat-error-64bit.test

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ RWStructuredBuffer<double> Out3 : register(u2);
66

77
[numthreads(1,1,1)]
88
void main() {
9-
Out1[0] = 20.3;
9+
Out1[0] = 20.25;
1010
Out1[1] = 5.0;
1111
Out2[0] = 0.0;
1212
Out3[0] = asdouble(0x00000000, 0x7FF80000); // Should be NaN
@@ -128,21 +128,16 @@ DescriptorSets:
128128
# CHECK: Name: Out1
129129
# CHECK: Format: Float64
130130
# CHECK: Stride: 8
131-
# CHECK: Data: [ 20.3, 5 ]
131+
# CHECK: Data: [ 20.25, 5 ]
132132
# CHECK: OutputProps:
133133
# CHECK: Height: 0
134134
# CHECK: Width: 0
135135
# CHECK: Depth: 0
136136
# CHECK: ...
137-
# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values:
138-
# CHECK-NEXT: [ 0x1.8{{[0]*}}p+0, 0x1.4{{[0]*}}p+1 ]
139-
# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values:
140-
# CHECK-NEXT: [ 0x1.44cccc
141-
# The rest is #ccccccdp+4, 0x1.4000000000000p+2 ], but some implementations
142-
# have trailing 0's for the remaining hex64 data. So, we resume checking from p+4
143-
# CHECK: p+4, 0x1.4{{[0]*}}p+2 ]
144-
145-
# CHECK: Test failed: Test2
137+
# CHECK-NEXT: Full Hex representation of Expected Buffer Values:
138+
# CHECK-NEXT: [ 0x3ff8000000000000, 0x4004000000000000 ]
139+
# CHECK-NEXT: Full Hex representation of Actual Buffer Values:
140+
# CHECK-NEXT: [ 0x4034400000000000, 0x4014000000000000 ]
146141
# CHECK: Comparison Rule: BufferFloatULP
147142
# CHECK: ULP: 1
148143
# CHECK: Expected:
@@ -167,10 +162,10 @@ DescriptorSets:
167162
# CHECK: Width: 0
168163
# CHECK: Depth: 0
169164
# CHECK: ...
170-
# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values:
171-
# CHECK-NEXT: [ 0x0.fffffffffffffp-1022 ]
172-
# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values:
173-
# CHECK-NEXT: [ 0x{{0|0.0000000000000}}p+0 ]
165+
# CHECK-NEXT: Full Hex representation of Expected Buffer Values:
166+
# CHECK-NEXT: [ 0xfffffffffffff ]
167+
# CHECK-NEXT: Full Hex representation of Actual Buffer Values:
168+
# CHECK-NEXT: [ 0x0 ]
174169

175170
# CHECK: Test failed: Test3
176171
# CHECK: Comparison Rule: BufferFloatULP
@@ -197,10 +192,10 @@ DescriptorSets:
197192
# CHECK: Width: 0
198193
# CHECK: Depth: 0
199194
# CHECK: ...
200-
# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values:
201-
# CHECK-NEXT: [ 0x{{0|0.0000000000000}}p+0 ]
202-
# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values:
203-
# CHECK-NEXT: [ nan ]
195+
# CHECK-NEXT: Full Hex representation of Expected Buffer Values:
196+
# CHECK-NEXT: [ 0x0 ]
197+
# CHECK-NEXT: Full Hex representation of Actual Buffer Values:
198+
# CHECK-NEXT: [ 0x7ff8000000000000 ]
204199

205200
# CHECK: Test failed: Test4
206201
# CHECK: Comparison Rule: BufferFloatEpsilon
@@ -221,19 +216,16 @@ DescriptorSets:
221216
# CHECK: Name: Out1
222217
# CHECK: Format: Float64
223218
# CHECK: Stride: 8
224-
# CHECK: Data: [ 20.3, 5 ]
219+
# CHECK: Data: [ 20.25, 5 ]
225220
# CHECK: OutputProps:
226221
# CHECK: Height: 0
227222
# CHECK: Width: 0
228223
# CHECK: Depth: 0
229224
# CHECK: ...
230-
# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values:
231-
# CHECK-NEXT: [ 0x1.8{{[0]*}}p+0, 0x1.4{{[0]*}}p+1 ]
232-
# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values:
233-
# CHECK-NEXT: [ 0x1.44cccc
234-
# The rest is #ccccccdp+4, 0x1.4000000000000p+2 ], but some implementations
235-
# have trailing 0's for the remaining hex64 data. So, we resume checking from p+4
236-
# CHECK: p+4, 0x1.4{{[0]*}}p+2 ]
225+
# CHECK-NEXT: Full Hex representation of Expected Buffer Values:
226+
# CHECK-NEXT: [ 0x3ff8000000000000, 0x4004000000000000 ]
227+
# CHECK-NEXT: Full Hex representation of Actual Buffer Values:
228+
# CHECK-NEXT: [ 0x4034400000000000, 0x4014000000000000 ]
237229

238230
# CHECK: Test failed: Test5
239231
# CHECK: Comparison Rule: BufferFloatEpsilon
@@ -260,10 +252,10 @@ DescriptorSets:
260252
# CHECK: Width: 0
261253
# CHECK: Depth: 0
262254
# CHECK: ...
263-
# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values:
264-
# CHECK-NEXT: [ 0x0.fffffffffffffp-1022 ]
265-
# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values:
266-
# CHECK-NEXT: [ 0x{{0|0.0000000000000}}p+0 ]
255+
# CHECK-NEXT: Full Hex representation of Expected Buffer Values:
256+
# CHECK-NEXT: [ 0xfffffffffffff ]
257+
# CHECK-NEXT: Full Hex representation of Actual Buffer Values:
258+
# CHECK-NEXT: [ 0x0 ]
267259

268260
# CHECK: Test failed: Test6
269261
# CHECK: Comparison Rule: BufferFloatEpsilon
@@ -290,7 +282,7 @@ DescriptorSets:
290282
# CHECK: Width: 0
291283
# CHECK: Depth: 0
292284
# CHECK: ...
293-
# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values:
294-
# CHECK-NEXT: [ 0x{{0|0.0000000000000}}p+0 ]
295-
# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values:
296-
# CHECK-NEXT: [ nan ]
285+
# CHECK-NEXT: Full Hex representation of Expected Buffer Values:
286+
# CHECK-NEXT: [ 0x0 ]
287+
# CHECK-NEXT: Full Hex representation of Actual Buffer Values:
288+
# CHECK-NEXT: [ 0x7ff8000000000000 ]

0 commit comments

Comments
 (0)