Forráskód Böngészése

convert recoverable exceptions to c++ (#3636)

Instead of raising structured exceptions for unreachable and fatal
errors, raising c++ exceptions allows returning an error code and
getting a useful message instead of requiring a structured exception
handler to catch it.

Add cast failure assert
Greg Roth 4 éve
szülő
commit
6b44d611f7

+ 9 - 0
include/dxc/Support/ErrorCodes.h

@@ -107,3 +107,12 @@
 
 // 0X80AA001A - Error in extension mechanism.
 #define DXC_E_EXTENSION_ERROR                         DXC_MAKE_HRESULT(DXC_SEVERITY_ERROR,FACILITY_DXC,(0x001A))
+
+// 0X80AA001B - LLVM Fatal Error
+#define DXC_E_LLVM_FATAL_ERROR                         DXC_MAKE_HRESULT(DXC_SEVERITY_ERROR,FACILITY_DXC,(0x001B))
+
+// 0X80AA001C - LLVM Unreachable code
+#define DXC_E_LLVM_UNREACHABLE                         DXC_MAKE_HRESULT(DXC_SEVERITY_ERROR,FACILITY_DXC,(0x001C))
+
+// 0X80AA001D - LLVM Cast Failure
+#define DXC_E_LLVM_CAST_ERROR                         DXC_MAKE_HRESULT(DXC_SEVERITY_ERROR,FACILITY_DXC,(0x001D))

+ 7 - 6
include/llvm/Support/Casting.h

@@ -16,6 +16,7 @@
 #define LLVM_SUPPORT_CASTING_H
 
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/type_traits.h"
 #include <cassert>
 
@@ -221,21 +222,21 @@ template <class X, class Y>
 inline typename std::enable_if<!is_simple_type<Y>::value,
                                typename cast_retty<X, const Y>::ret_type>::type
 cast(const Y &Val) {
-  assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
+  llvm_cast_assert(X, Val); // HLSL change
   return cast_convert_val<
       X, const Y, typename simplify_type<const Y>::SimpleType>::doit(Val);
 }
 
 template <class X, class Y>
 inline typename cast_retty<X, Y>::ret_type cast(Y &Val) {
-  assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
+  llvm_cast_assert(X, Val); // HLSL change
   return cast_convert_val<X, Y,
                           typename simplify_type<Y>::SimpleType>::doit(Val);
 }
 
 template <class X, class Y>
 inline typename cast_retty<X, Y *>::ret_type cast(Y *Val) {
-  assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
+  llvm_cast_assert(X, Val); // HLSL change
   return cast_convert_val<X, Y*,
                           typename simplify_type<Y*>::SimpleType>::doit(Val);
 }
@@ -249,7 +250,7 @@ LLVM_ATTRIBUTE_UNUSED_RESULT inline typename std::enable_if<
 cast_or_null(const Y &Val) {
   if (!Val)
     return nullptr;
-  assert(isa<X>(Val) && "cast_or_null<Ty>() argument of incompatible type!");
+  llvm_cast_assert(X, Val); // HLSL change
   return cast<X>(Val);
 }
 
@@ -259,7 +260,7 @@ LLVM_ATTRIBUTE_UNUSED_RESULT inline typename std::enable_if<
 cast_or_null(Y &Val) {
   if (!Val)
     return nullptr;
-  assert(isa<X>(Val) && "cast_or_null<Ty>() argument of incompatible type!");
+  llvm_cast_assert(X, Val); // HLSL change
   return cast<X>(Val);
 }
 
@@ -267,7 +268,7 @@ template <class X, class Y>
 LLVM_ATTRIBUTE_UNUSED_RESULT inline typename cast_retty<X, Y *>::ret_type
 cast_or_null(Y *Val) {
   if (!Val) return nullptr;
-  assert(isa<X>(Val) && "cast_or_null<Ty>() argument of incompatible type!");
+  llvm_cast_assert(X, Val); // HLSL change
   return cast<X>(Val);
 }
 

+ 9 - 3
include/llvm/Support/ErrorHandling.h

@@ -84,6 +84,9 @@ namespace llvm {
   LLVM_ATTRIBUTE_NORETURN void
   llvm_unreachable_internal(const char *msg=nullptr, const char *file=nullptr,
                             unsigned line=0);
+
+  // HLSL Change - throw special exception for cast mismatch
+  void llvm_cast_assert_internal(const char *func);
 }
 
 /// Marks that the current location is not supposed to be reachable.
@@ -94,13 +97,16 @@ namespace llvm {
 ///
 /// Use this instead of assert(0).  It conveys intent more clearly and
 /// allows compilers to omit some unnecessary code.
-#ifndef NDEBUG
+#if 1 // HLSL Change - always throw exception with message for unreachable
 #define llvm_unreachable(msg) \
   ::llvm::llvm_unreachable_internal(msg, __FILE__, __LINE__)
-//#elif defined(LLVM_BUILTIN_UNREACHABLE) // HLSL Change - always throw exception for unreachable
-//#define llvm_unreachable(msg) LLVM_BUILTIN_UNREACHABLE
+#elif defined(LLVM_BUILTIN_UNREACHABLE)
+#define llvm_unreachable(msg) LLVM_BUILTIN_UNREACHABLE
 #else
 #define llvm_unreachable(msg) ::llvm::llvm_unreachable_internal()
 #endif
 
+// HLSL Change - throw special exception for cast type mismatch
+#define llvm_cast_assert(X, Val) ((void)( (!!(isa<X>(Val))) || (::llvm::llvm_cast_assert_internal(__FUNCTION__), 0) ))
+
 #endif

+ 16 - 6
lib/Support/ErrorHandling.cpp

@@ -32,6 +32,7 @@
 #ifdef _WIN32
 #include "windows.h"  // HLSL Change
 #endif
+#include "dxc/Support/exception.h"  // HLSL Change
 
 #if defined(HAVE_UNISTD_H)
 # include <unistd.h>
@@ -112,7 +113,8 @@ void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) {
   if (handler) {
     handler(handlerData, Reason.str(), GenCrashDiag);
   }
-  RaiseException(STATUS_LLVM_FATAL, 0, 0, 0);
+
+  throw hlsl::Exception(DXC_E_LLVM_FATAL_ERROR, std::string("LLVM ERROR: ") + Reason.str() + "\n");
 #endif
 }
 
@@ -121,19 +123,27 @@ void llvm::llvm_unreachable_internal(const char *msg, const char *file,
   // This code intentionally doesn't call the ErrorHandler callback, because
   // llvm_unreachable is intended to be used to indicate "impossible"
   // situations, and not legitimate runtime errors.
+  // HLSL Change - collect full message in string
+  SmallVector<char, 64> Buffer;
+  raw_svector_ostream OS(Buffer);
   if (msg)
-    dbgs() << msg << "\n";
-  dbgs() << "UNREACHABLE executed";
+    OS << msg << "\n";
+  OS << "UNREACHABLE executed";
   if (file)
-    dbgs() << " at " << file << ":" << line;
-  dbgs() << "!\n";
+    OS << " at " << file << ":" << line;
+  OS << "!\n";
 #ifndef LLVM_ON_WIN32 // HLSL Change - unwind if necessary, but don't terminate the process
+  dbgs() << OS.str();
   abort();
 #else
-  RaiseException(STATUS_LLVM_UNREACHABLE, 0, 0, 0);
+  throw hlsl::Exception(DXC_E_LLVM_UNREACHABLE, OS.str());
 #endif
 }
 
+void llvm::llvm_cast_assert_internal(const char *func) {
+  throw hlsl::Exception(DXC_E_LLVM_CAST_ERROR, std::string(func) + "<X>() argument of incompatible type!\n");
+}
+
 static void bindingsErrorHandler(void *user_data, const std::string& reason,
                                  bool gen_crash_diag) {
   LLVMFatalErrorHandler handler =

+ 1 - 1
tools/clang/tools/dxclib/dxc.cpp

@@ -1258,7 +1258,7 @@ static LONG CALLBACK ExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
     fputs("LLVM Fatal Error\n", stderr);
     break;
   default:
-    fputs("Error ", stderr);
+    fputs("Terminal Error ", stderr);
     sprintf_s(scratch, _countof(scratch), "0x%08x\n", pExceptionInfo->ExceptionRecord->ExceptionCode);
     fputs(scratch, stderr);
   }

+ 3 - 1
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -1126,9 +1126,11 @@ public:
       _Analysis_assume_(DXC_FAILED(e.hr));
       CComPtr<IDxcResult> pResult;
       hr = e.hr;
+      std::string msg("Internal Compiler error: ");
+      msg += e.msg;
       if (SUCCEEDED(DxcResult::Create(e.hr, DXC_OUT_NONE, {
               DxcOutputObject::ErrorOutput(CP_UTF8,
-                e.msg.c_str(), e.msg.size())
+                msg.c_str(), msg.size())
             }, &pResult)) &&
           SUCCEEDED(pResult->QueryInterface(riid, ppResult))) {
         hr = S_OK;