diff --git a/lib/Support/Check.cpp b/lib/Support/Check.cpp index 058dbc30..ae47e207 100644 --- a/lib/Support/Check.cpp +++ b/lib/Support/Check.cpp @@ -10,11 +10,13 @@ //===----------------------------------------------------------------------===// #include "Support/Check.h" +#include "Support/Pipeline.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" #include +#include constexpr uint16_t Float16BitSign = 0x8000; constexpr uint16_t Float16BitExp = 0x7c00; @@ -267,30 +269,119 @@ static bool testBufferFloatULP(offloadtest::Buffer *B1, offloadtest::Buffer *B2, return false; } +template +static std::string bitPatternAsHex64(const T &Val, + offloadtest::Rule ComparisonRule) { + static_assert(sizeof(T) <= sizeof(uint64_t), "Type too large for Hex64"); + + std::ostringstream Oss; + if (ComparisonRule == offloadtest::Rule::BufferExact) + Oss << "0x" << std::hex << Val; + else + Oss << std::hexfloat << Val; + return Oss.str(); +} + +template +static std::string formatBuffer(offloadtest::Buffer *B, + offloadtest::Rule Rule) { + const llvm::MutableArrayRef Arr(reinterpret_cast(B->Data.get()), + B->Size / sizeof(T)); + if (Arr.empty()) + return ""; + + std::string Result = "[ " + bitPatternAsHex64(Arr[0], Rule); + for (size_t I = 1; I < Arr.size(); ++I) + Result += ", " + bitPatternAsHex64(Arr[I], Rule); + Result += " ]"; + return Result; +} + +static const std::string getBufferStr(offloadtest::Buffer *B, + offloadtest::Rule Rule) { + using DF = offloadtest::DataFormat; + switch (B->Format) { + case DF::Hex8: + return formatBuffer(B, Rule); + case DF::Hex16: + return formatBuffer(B, Rule); + case DF::Hex32: + return formatBuffer(B, Rule); + case DF::Hex64: + return formatBuffer(B, Rule); + case DF::UInt16: + return formatBuffer(B, Rule); + case DF::UInt32: + return formatBuffer(B, Rule); + case DF::UInt64: + return formatBuffer(B, Rule); + case DF::Int16: + return formatBuffer(B, Rule); + case DF::Int32: + return formatBuffer(B, Rule); + case DF::Int64: + return formatBuffer(B, Rule); + case DF::Float16: + return formatBuffer(B, + Rule); // assuming no native float16 + case DF::Float32: + return formatBuffer(B, Rule); + case DF::Float64: + return formatBuffer(B, Rule); + case DF::Bool: + return formatBuffer(B, + Rule); // Because sizeof(bool) is 1 but HLSL + // represents a bool using 4 bytes. + } +} + llvm::Error verifyResult(offloadtest::Result R) { + llvm::SmallString<256> Str; + llvm::raw_svector_ostream OS(Str); + OS << "Test failed: " << R.Name << "\n"; + switch (R.ComparisonRule) { case offloadtest::Rule::BufferExact: { if (testBufferExact(R.ActualPtr, R.ExpectedPtr)) return llvm::Error::success(); + OS << "Comparison Rule: BufferExact\n"; break; } case offloadtest::Rule::BufferFloatULP: { if (testBufferFloatULP(R.ActualPtr, R.ExpectedPtr, R.ULPT, R.DM)) return llvm::Error::success(); + OS << "Comparison Rule: BufferFloatULP\nULP: " << R.ULPT << "\n"; break; } case offloadtest::Rule::BufferFloatEpsilon: { if (testBufferFloatEpsilon(R.ActualPtr, R.ExpectedPtr, R.Epsilon, R.DM)) return llvm::Error::success(); + + std::ostringstream Oss; + Oss << std::defaultfloat << R.Epsilon; + OS << "Comparison Rule: BufferFloatEpsilon\nEpsilon: " << Oss.str() << "\n"; break; } } - llvm::SmallString<256> Str; - llvm::raw_svector_ostream OS(Str); - OS << "Test failed: " << R.Name << "\nExpected:\n"; + + OS << "Expected:\n"; llvm::yaml::Output YAMLOS(OS); YAMLOS << *R.ExpectedPtr; OS << "Got:\n"; YAMLOS << *R.ActualPtr; + + // Now print exact hex64 representations of each element of the + // actual and expected buffers. + + const std::string ExpectedBufferStr = + getBufferStr(R.ExpectedPtr, R.ComparisonRule); + const std::string ActualBufferStr = + getBufferStr(R.ActualPtr, R.ComparisonRule); + + OS << "Full Hex 64bit representation of Expected Buffer Values:\n" + << ExpectedBufferStr << "\n"; + OS << "Full Hex 64bit representation of Actual Buffer Values:\n" + << ActualBufferStr << "\n"; + return llvm::createStringError(Str.c_str()); } diff --git a/lib/Support/Pipeline.cpp b/lib/Support/Pipeline.cpp index 2baca300..3cfa167f 100644 --- a/lib/Support/Pipeline.cpp +++ b/lib/Support/Pipeline.cpp @@ -93,6 +93,31 @@ void MappingTraits::mapping( I.mapRequired("Resources", D.Resources); } +template static void setData(IO &I, offloadtest::Buffer &B) { + if (I.outputting()) { + llvm::MutableArrayRef Arr(reinterpret_cast(B.Data.get()), + B.Size / sizeof(Type)); + I.mapRequired("Data", Arr); + } else { + int64_t ZeroInitSize; + I.mapOptional("ZeroInitSize", ZeroInitSize, 0); + if (ZeroInitSize > 0) { + B.Size = ZeroInitSize; + B.Data.reset(new char[B.Size]); + memset(B.Data.get(), 0, B.Size); + I.mapOptional("OutputProps", B.OutputProps); + return; + } + llvm::SmallVector Arr; + I.mapRequired("Data", Arr); + B.Size = Arr.size() * sizeof(Type); + B.Data.reset(new char[B.Size]); + memcpy(B.Data.get(), Arr.data(), B.Size); + } + + I.mapOptional("OutputProps", B.OutputProps); +} + void MappingTraits::mapping(IO &I, offloadtest::Buffer &B) { I.mapRequired("Name", B.Name); @@ -102,44 +127,37 @@ void MappingTraits::mapping(IO &I, I.mapOptional("Counter", B.Counter, 0); if (!I.outputting() && B.Stride != 0 && B.Channels != 1) I.setError("Cannot set a structure stride and more than one channel."); + using DF = offloadtest::DataFormat; switch (B.Format) { -#define DATA_CASE(Enum, Type) \ - case DataFormat::Enum: { \ - if (I.outputting()) { \ - llvm::MutableArrayRef Arr(reinterpret_cast(B.Data.get()), \ - B.Size / sizeof(Type)); \ - I.mapRequired("Data", Arr); \ - } else { \ - int64_t ZeroInitSize; \ - I.mapOptional("ZeroInitSize", ZeroInitSize, 0); \ - if (ZeroInitSize > 0) { \ - B.Size = ZeroInitSize; \ - B.Data.reset(new char[B.Size]); \ - memset(B.Data.get(), 0, B.Size); \ - break; \ - } \ - llvm::SmallVector Arr; \ - I.mapRequired("Data", Arr); \ - B.Size = Arr.size() * sizeof(Type); \ - B.Data.reset(new char[B.Size]); \ - memcpy(B.Data.get(), Arr.data(), B.Size); \ - } \ - break; \ - } - DATA_CASE(Hex8, llvm::yaml::Hex8) - DATA_CASE(Hex16, llvm::yaml::Hex16) - DATA_CASE(Hex32, llvm::yaml::Hex32) - DATA_CASE(Hex64, llvm::yaml::Hex64) - DATA_CASE(UInt16, uint16_t) - DATA_CASE(UInt32, uint32_t) - DATA_CASE(UInt64, uint64_t) - DATA_CASE(Int16, int16_t) - DATA_CASE(Int32, int32_t) - DATA_CASE(Int64, int64_t) - DATA_CASE(Float16, llvm::yaml::Hex16) - DATA_CASE(Float32, float) - DATA_CASE(Float64, double) - DATA_CASE(Bool, uint32_t) // Because sizeof(bool) is 1 but HLSL represents a bool using 4 bytes. + case DF::Hex8: + return setData(I, B); + case DF::Hex16: + return setData(I, B); + case DF::Hex32: + return setData(I, B); + case DF::Hex64: + return setData(I, B); + case DF::UInt16: + return setData(I, B); + case DF::UInt32: + return setData(I, B); + case DF::UInt64: + return setData(I, B); + case DF::Int16: + return setData(I, B); + case DF::Int32: + return setData(I, B); + case DF::Int64: + return setData(I, B); + case DF::Float16: + return setData(I, B); // assuming no native float16 + case DF::Float32: + return setData(I, B); + case DF::Float64: + return setData(I, B); + case DF::Bool: + return setData(I, B); // Because sizeof(bool) is 1 but HLSL + // represents a bool using 4 bytes. } I.mapOptional("OutputProps", B.OutputProps); diff --git a/test/Tools/Offloader/BufferExact-error.test b/test/Tools/Offloader/BufferExact-error.test index fac4f8a0..306f8a76 100644 --- a/test/Tools/Offloader/BufferExact-error.test +++ b/test/Tools/Offloader/BufferExact-error.test @@ -48,6 +48,7 @@ DescriptorSets: # RUN: not %offloader %t/pipeline.yaml %t.o 2>&1 | FileCheck %s # CHECK: Test failed: Test1 +# CHECK: Comparison Rule: BufferExact # CHECK: Expected: # CHECK: --- # CHECK: Name: Expected1 @@ -69,3 +70,7 @@ DescriptorSets: # CHECK: Height: 0 # CHECK: Width: 0 # CHECK: Depth: 0 +# CHECK: Full Hex 64bit representation of Expected Buffer Values: +# CHECK: [ 0x1, 0x2, 0x3, 0x4 ] +# CHECK: Full Hex 64bit representation of Actual Buffer Values: +# CHECK: [ 0x14, 0x1e, 0x28, 0x32 ] diff --git a/test/Tools/Offloader/BufferFloat-error-64bit.test b/test/Tools/Offloader/BufferFloat-error-64bit.test index 6a66316f..5f7094ba 100644 --- a/test/Tools/Offloader/BufferFloat-error-64bit.test +++ b/test/Tools/Offloader/BufferFloat-error-64bit.test @@ -110,6 +110,8 @@ DescriptorSets: # RUN: not %offloader %t/pipeline.yaml %t.o 2>&1 | FileCheck %s # CHECK: Test failed: Test1 +# CHECK: Comparison Rule: BufferFloatULP +# CHECK: ULP: 1 # CHECK: Expected: # CHECK: --- # CHECK: Name: Expected1 @@ -131,8 +133,18 @@ DescriptorSets: # CHECK: Height: 0 # CHECK: Width: 0 # CHECK: Depth: 0 +# CHECK: ... +# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values: +# CHECK-NEXT: [ 0x1.8000000000000p+0, 0x1.4000000000000p+1 ] +# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values: +# CHECK-NEXT: [ 0x1.44cccc +# The rest is #ccccccdp+4, 0x1.4000000000000p+2 ], but some implementations +# have trailing 0's for the remaining hex64 data. So, we resume checking from p+4 +# CHECK: p+4, 0x1.4000000000000p+2 ] # CHECK: Test failed: Test2 +# CHECK: Comparison Rule: BufferFloatULP +# CHECK: ULP: 1 # CHECK: Expected: # CHECK: --- # CHECK: Name: Expected2 @@ -154,8 +166,15 @@ DescriptorSets: # CHECK: Height: 0 # CHECK: Width: 0 # CHECK: Depth: 0 +# CHECK: ... +# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values: +# CHECK-NEXT: [ 0x0.fffffffffffffp-1022 ] +# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values: +# CHECK-NEXT: [ 0x0.0000000000000p+0 ] # CHECK: Test failed: Test3 +# CHECK: Comparison Rule: BufferFloatULP +# CHECK: ULP: 0 # CHECK: Expected: # CHECK: --- # CHECK: Name: Expected3 @@ -177,8 +196,15 @@ DescriptorSets: # CHECK: Height: 0 # CHECK: Width: 0 # CHECK: Depth: 0 +# CHECK: ... +# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values: +# CHECK-NEXT: [ 0x0.0000000000000p+0 ] +# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values: +# CHECK-NEXT: [ nan ] # CHECK: Test failed: Test4 +# CHECK: Comparison Rule: BufferFloatEpsilon +# CHECK: Epsilon: 0.1 # CHECK: Expected: # CHECK: --- # CHECK: Name: Expected1 @@ -200,8 +226,18 @@ DescriptorSets: # CHECK: Height: 0 # CHECK: Width: 0 # CHECK: Depth: 0 +# CHECK: ... +# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values: +# CHECK-NEXT: [ 0x1.8000000000000p+0, 0x1.4000000000000p+1 ] +# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values: +# CHECK-NEXT: [ 0x1.44cccc +# The rest is #ccccccdp+4, 0x1.4000000000000p+2 ], but some implementations +# have trailing 0's for the remaining hex64 data. So, we resume checking from p+4 +# CHECK: p+4, 0x1.4000000000000p+2 ] # CHECK: Test failed: Test5 +# CHECK: Comparison Rule: BufferFloatEpsilon +# CHECK: Epsilon: 0 # CHECK: Expected: # CHECK: --- # CHECK: Name: Expected2 @@ -223,8 +259,15 @@ DescriptorSets: # CHECK: Height: 0 # CHECK: Width: 0 # CHECK: Depth: 0 +# CHECK: ... +# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values: +# CHECK-NEXT: [ 0x0.fffffffffffffp-1022 ] +# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values: +# CHECK-NEXT: [ 0x0.0000000000000p+0 ] # CHECK: Test failed: Test6 +# CHECK: Comparison Rule: BufferFloatEpsilon +# CHECK: Epsilon: 0 # CHECK: Expected: # CHECK: --- # CHECK: Name: Expected3 @@ -246,3 +289,8 @@ DescriptorSets: # CHECK: Height: 0 # CHECK: Width: 0 # CHECK: Depth: 0 +# CHECK: ... +# CHECK-NEXT: Full Hex 64bit representation of Expected Buffer Values: +# CHECK-NEXT: [ 0x0.0000000000000p+0 ] +# CHECK-NEXT: Full Hex 64bit representation of Actual Buffer Values: +# CHECK-NEXT: [ nan ]