diff --git a/tools/clang/test/CodeGenHLSL/fpexcept.hlsl b/tools/clang/test/CodeGenHLSL/fpexcept.hlsl new file mode 100644 index 000000000..bdb55ec60 --- /dev/null +++ b/tools/clang/test/CodeGenHLSL/fpexcept.hlsl @@ -0,0 +1,7 @@ +// RUN: %dxc -E main -T ps_6_0 %s + +float3 main() : SV_Target +{ + // Some calls known to cause floating point exceptions when evaluated + return float3(pow(0.0f, 0.0f), pow(-1.0f, 0.5f), pow(0.0f, -1.0f)); +} \ No newline at end of file diff --git a/tools/clang/tools/dxcompiler/dxcompilerobj.cpp b/tools/clang/tools/dxcompiler/dxcompilerobj.cpp index acab19f02..cc6af2ccc 100644 --- a/tools/clang/tools/dxcompiler/dxcompilerobj.cpp +++ b/tools/clang/tools/dxcompiler/dxcompilerobj.cpp @@ -46,6 +46,7 @@ #endif #include "dxillib.h" #include +#include // SPIRV change starts #ifdef ENABLE_SPIRV_CODEGEN @@ -100,6 +101,28 @@ static void CreateOperationResultFromOutputs( ppResult); } +struct DefaultFPEnvScope +{ + // _controlfp_s is non-standard and .feholdexceptions doesn't work on windows...? +#ifdef _WIN32 + unsigned int previousValue; + DefaultFPEnvScope() { + // No exceptions, preserve denormals & round to nearest. + errno_t error = _controlfp_s(&previousValue, _MCW_EM | _DN_SAVE | _RC_NEAR, _MCW_EM | _MCW_DN | _MCW_RC); + IFT(error == 0 ? S_OK : E_FAIL); + } + ~DefaultFPEnvScope() { + unsigned int newValue; + errno_t error = _controlfp_s(&newValue, previousValue, _MCW_EM | _MCW_DN | _MCW_RC); + // During cleanup we can't throw as we might already be handling another one. + DXASSERT(error == 0, "Failed to restore floating-point environment."); + (void)error; + } +#else + DefaultFPEnvScope() {} // Dummy ctor to avoid unused local warning +#endif +}; + class HLSLExtensionsCodegenHelperImpl : public HLSLExtensionsCodegenHelper { private: CompilerInstance &m_CI; @@ -311,6 +334,8 @@ public: DxcThreadMalloc TM(m_pMalloc); try { + DefaultFPEnvScope fpEnvScope; + IFT(CreateMemoryStream(m_pMalloc, &pOutputStream)); // Parse command-line options into DxcOpts @@ -649,6 +674,8 @@ public: IFC(hlsl::DxcGetBlobAsUtf8(pSource, &utf8Source)); try { + DefaultFPEnvScope fpEnvScope; + CComPtr pOutputStream; dxcutil::DxcArgsFileSystem *msfPtr = dxcutil::CreateDxcArgsFileSystem(utf8Source, pSourceName, pIncludeHandler); std::unique_ptr<::llvm::sys::fs::MSFileSystem> msf(msfPtr); @@ -756,6 +783,8 @@ public: DxcEtw_DXCompilerDisassemble_Start(); DxcThreadMalloc TM(m_pMalloc); try { + DefaultFPEnvScope fpEnvScope; + ::llvm::sys::fs::MSFileSystem *msfPtr; IFT(CreateMSFileSystemForDisk(&msfPtr)); std::unique_ptr<::llvm::sys::fs::MSFileSystem> msf(msfPtr); diff --git a/tools/clang/unittests/HLSL/CompilerTest.cpp b/tools/clang/unittests/HLSL/CompilerTest.cpp index 3d0df62f6..3e4b6277e 100644 --- a/tools/clang/unittests/HLSL/CompilerTest.cpp +++ b/tools/clang/unittests/HLSL/CompilerTest.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "dxc/DxilContainer/DxilContainer.h" #include "dxc/Support/WinIncludes.h" #include "dxc/dxcapi.h" @@ -374,6 +375,7 @@ public: TEST_METHOD(CodeGenExternRes) TEST_METHOD(CodeGenExpandTrig) TEST_METHOD(CodeGenFloatCast) + TEST_METHOD(CodeGenFloatingPointEnvironment) TEST_METHOD(CodeGenFloatToBool) TEST_METHOD(CodeGenFirstbitHi) TEST_METHOD(CodeGenFirstbitLo) @@ -3452,6 +3454,28 @@ TEST_F(CompilerTest, CodeGenFloatCast) { CodeGenTestCheck(L"..\\CodeGenHLSL\\float_cast.hlsl"); } +struct FPEnableExceptionsScope +{ + // _controlfp_s is non-standard and doesn't have a function to enable exceptions +#ifdef _WIN32 + unsigned int previousValue; + FPEnableExceptionsScope() { + VERIFY_IS_TRUE(_controlfp_s(&previousValue, 0, _MCW_EM) == 0); // _MCW_EM == 0 means enable all exceptions + } + ~FPEnableExceptionsScope() { + unsigned int newValue; + errno_t error = _controlfp_s(&newValue, previousValue, _MCW_EM); + DXASSERT(error == 0, "Failed to restore floating-point environment."); + (void)error; + } +#endif +}; + +TEST_F(CompilerTest, CodeGenFloatingPointEnvironment) { + FPEnableExceptionsScope fpEnableExceptions; + CodeGenTestCheck(L"..\\CodeGenHLSL\\fpexcept.hlsl"); +} + TEST_F(CompilerTest, CodeGenFloatToBool) { CodeGenTestCheck(L"..\\CodeGenHLSL\\float_to_bool.hlsl"); }