Bläddra i källkod

Set default floating-point environment at compiler boundaries (#1703)

Restore floating-point environment at compiler boundaries, and add a test for this.
Tristan Labelle 6 år sedan
förälder
incheckning
7a18290e77

+ 7 - 0
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));
+}

+ 29 - 0
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -46,6 +46,7 @@
 #endif
 #include "dxillib.h"
 #include <algorithm>
+#include <cfloat>
 
 // SPIRV change starts
 #ifdef ENABLE_SPIRV_CODEGEN
@@ -100,6 +101,28 @@ static void CreateOperationResultFromOutputs(
                                    ppResult);
 }
 
+struct DefaultFPEnvScope
+{
+  // _controlfp_s is non-standard and <cfenv>.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<AbstractMemoryStream> 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);

+ 24 - 0
tools/clang/unittests/HLSL/CompilerTest.cpp

@@ -20,6 +20,7 @@
 #include <cassert>
 #include <sstream>
 #include <algorithm>
+#include <cfloat>
 #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 <cfenv> 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");
 }